<?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/audio" rel="self" type="application/rss+xml" />
                            <title><![CDATA[ Latest from Tom's Guide in Audio ]]></title>
                <link>https://www.tomsguide.com/audio</link>
        <description><![CDATA[ All the latest audio content from the Tom's Guide team ]]></description>
                                    <lastBuildDate>Sat, 12 Sep 2026 04:45:00 +0000</lastBuildDate>
                            <language>en</language>
                                <item>
                                                            <title><![CDATA[ AirPods 5 vs AirPods 4: All the key differences ]]></title>
                                                                                                <dc:content><![CDATA[ <p>After weeks of rumors, Apple finally announced the <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-5-officially-announced-here-are-the-upgrades">AirPods 5</a> on September 9 at its "Surprise and Shine" <a href="https://www.tomsguide.com/phones/iphones/iphone-duo-hands-on-apple-nailed-it-and-just-put-everyone-else-on-notice">iPhone Duo</a> event. </p><p>Like the AirPods 4 before them, the AirPods 5 will come in two distinct flavors: a base model with ANC and a higher-end model with a wireless charging case and a volume slider on the stem.</p><p>Both versions of the AirPods 5 will be powered by the H2 Wireless Chip that offers personalized spatial audio and live translation, but is all that reason enough to upgrade from the now two-year-old AirPods 4? The answer isn't exactly clear cut.</p><div class="product"><a data-dimension112="cfca91f8-ae20-11f1-b9ba-e95f4b5bdff3" data-action="Deal Block" data-label="AirPods 5" data-dimension48="AirPods 5" data-dimension25="$129" href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" 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:94.40%;"><img id="mJGKbeqw2FwvrHngHh7KUc" name="Apple AirPods 5 deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/mJGKbeqw2FwvrHngHh7KUc.jpg" mos="" align="middle" fullscreen="" width="1500" height="1416" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods 5 are available for pre-order at Apple today. They offer live translation, full support for Siri AI, and 50% more noise reduction than the previous generation. They're also slightly cheaper than their predecessor with the ANC model selling for $129 and the AirPods 5 with Wireless Charging Case for $149. You can get your AirPods 5 engraved and personalized for free when you preorder them. Plus, Apple is also giving you three months of Apple Music with your purchase. <a class="view-deal button" href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow" data-dimension112="cfca91f8-ae20-11f1-b9ba-e95f4b5bdff3" data-action="Deal Block" data-label="AirPods 5" data-dimension48="AirPods 5" data-dimension25="$129">View Deal</a></p></div><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-price-and-availability"><span>AirPods 5 vs AirPods 4: Price and availability</span></h3><p>Because there are two AirPods 5 models, there are two prices: $129 / AU$219 for the base model and $149 / AU$249 for the higher-end model. </p><p>The AirPods 4, now a few years old, are obviously less expensive. They start at just $99 for the base model. Interestingly, however, the more advanced AirPods 4 with ANC is still sitting at $149. That's going to make the decision pretty easy if you need ANC. </p><p>My favorite Apple earbuds are the <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review">AirPods Pro 3</a>, and those start at $249. It's a pretty big leap in price, but I think they're all-around a better option, especially when compared to the AirPods 4. </p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-design"><span>AirPods 5 vs AirPods 4: Design</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3667px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="UMXRo3jZ6wo7vr4PP9HA8Z" name="IMG_7864.jpg" alt="AirPods 4" src="https://cdn.mos.cms.futurecdn.net/UMXRo3jZ6wo7vr4PP9HA8Z.jpg" mos="" align="middle" fullscreen="" width="3667" height="2063" 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>Admittedly, there's not a lot of difference on the design front. Apple claims the AirPods 5 offer "an all-new multiport acoustic architecture" but you certainly wouldn't know it just by looking at them. </p><p>The major design difference between the AirPods 5 and AirPods 4 can be found on the stems of the newer model. It's so small you'll likely miss it, but if you look close you'll see a touch-capacitive divot that acts as a volume slider. It's a very minor detail that the AirPods 5 take from the more-expensive AirPods Pro 3. </p><p>Speaking of nice-to-have features, the AirPods 5 with Wireless Charging Case (the proper name of the more-advanced AirPods 5 model) do offer wireless charging (duh). You can use the same charger as the Apple Watch, Qi-compatible chargers or plain old USB-C if you don't already own a wireless charging mat.</p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-controls"><span>AirPods 5 vs AirPods 4: Controls</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4096px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rHV7NoWYrNE2zRY52gLYvS" name="AirPods 4" alt="The AirPods 4 being displayed in hand" src="https://cdn.mos.cms.futurecdn.net/rHV7NoWYrNE2zRY52gLYvS.jpg" mos="" align="middle" fullscreen="" width="4096" height="2304" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Alex Bracetti/Future)</span></figcaption></figure><p>Besides the aforementioned volume slider functionality, both the AirPods 5 and AirPods 4 use touch sensitive stems to control your music. </p><p>Both pairs of true wireless earbuds can differentiate between squeezes and taps, but it's up to you to learn which one does what. </p><p>I've never been a huge fan of touch technology in earbuds and prefer to use my voice or my phone to control playback, but it's nice to have the option in a pinch.</p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-sound-quality"><span>AirPods 5 vs AirPods 4: Sound quality</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2679px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qTWTqkcso8N3vy8W3VCoCV" name="IMG_7868.jpg" alt="AirPods 4" src="https://cdn.mos.cms.futurecdn.net/qTWTqkcso8N3vy8W3VCoCV.jpg" mos="" align="middle" fullscreen="" width="2679" height="1507" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Here's where things are going to get a bit murky. Apple claims that, due to its improved sonic architecture, the AirPods 5 are going to sound better than the AirPods 4. Sound, as we all know, is subjective, and I can't really comment on their sound quality until I've heard them myself. </p><p>So until then, let's hear from someone who's listened to both. </p><p>Kate Kozuch, the Head of Video here at Tom's Guide was in Cupertino this week to check them out. According to her, the "bass was a noticeable improvement" on the AirPods 5. That counts for a lot in a pair of open earbuds like the AirPods. </p><p>Still, Kate didn't feel like there was a big enough difference between the two to warrant an upgrade. "This is coming from an AirPods 4 user: I don't think anyone needs to upgrade from AirPods 4." </p><p>If you want the best sound quality in a pair of wireless earbuds, then, your best bet is still going to be the pricier AirPods Pro series. </p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-features"><span>AirPods 5 vs AirPods 4: Features</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4096px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AfpNLN9gCw8bQsGWjQyLaf" name="Apple AirPods 4" alt="The AirPods 4 connected to an iPhone 12" src="https://cdn.mos.cms.futurecdn.net/AfpNLN9gCw8bQsGWjQyLaf.jpg" mos="" align="middle" fullscreen="" width="4096" height="2304" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The AirPods 5 take some of the best features of the AirPods Pro (besides sound quality, obviously) and bring them down to a more affordable price. </p><p>Like the Pro, the AirPods 5 offer Spatial Audio support, live translation and "significantly improved" ANC. How significant? According to Apple, the AirPods 5 cancel out "50 percent more external noise compared to AirPods 4 with Active Noise Cancellation." So pretty big.</p><p>Now, keep in mind that these are open earbuds, so the ANC isn't going to be world-class here. I'd still expect a pair of Bose over-ears to do a better job than the AirPods 5, but hey, I'll take any improvement we can get.</p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-battery-life"><span>AirPods 5 vs AirPods 4: Battery life</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Jqh9spPaAZV8jiAkZ89XzK" name="AirPods 4 Tips - Lede (2)" alt="AirPods 4" src="https://cdn.mos.cms.futurecdn.net/Jqh9spPaAZV8jiAkZ89XzK.jpg" 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/ Alex Bracetti)</span></figcaption></figure><p>One of our biggest complaints against the AirPods 4 was that their battery life was a little lacking. You could get around 5 hours of listening with ANC off, and 4 hours when you turn it on. That's a lot less than the competition and it means having to constantly reach for a charger.</p><p>The base AirPods 5 don't seem to offer much of an improvement here, but the AirPods 5 with Wireless Charging Case offer an additional hour with ANC turned on thanks to a redesigned battery.</p><p>Will an extra hour make or break the experience? Probably not, especially when rival earbuds like the <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-1000xm6-review">Sony WF-1000XM6</a> offer around 8 hours with ANC turned on. </p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-early-outlook"><span>AirPods 5 vs AirPods 4: Early outlook</span></h3><p>If you're just getting AirPods for the first time, the AirPods 5 seem to be worth the extra $30 to get the ones with wireless charging. </p><p>If you're someone who already owns a pair of AirPods 4, however, it's not so cut and dry. Spatial audio, the audio slider and live translation are all great, don't get me wrong, but I'm not sure I'd toss out my old earbuds just to tap into them, especially when key areas like battery life haven't been improved all that much.</p><p>There are also some really great AirPods alternatives out there and, if you don't mind spending more for them, the award-winning AirPods Pro Series that I use every single day are some of the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> on the market. If you can, skip the AirPods and go straight for the AirPods Pro, but if the budget is tight, the base AirPods 5 will get you half the performance for around $100.</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/phones/iphones/interview-apple-ceo-john-ternus-on-his-defining-moment-for-the-iphone-duo">I interviewed Apple CEO John Ternus on the making of the iPhone Duo — here's what made him say 'yes, that's it'</a></li><li><a href="https://www.tomsguide.com/ai/apple-intelligence/people-are-freaking-out-about-the-new-always-listening-apple-watches-heres-what-apple-ceo-john-ternus-told-us">People are freaking out about the new 'always-listening' Apple Watches — here's what Apple CEO John Ternus told us</a></li><li><a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-ultra-4-vs-garmin-fenix-9-pro-price-specs-and-features-compared">Apple Watch Ultra 4 vs Garmin Fenix 9 Pro: price, specs and features compared</a></li></ul><div data-widget-type="multimodelreview" data-model-name="Apple AirPods 4,Apple AirPods Gen 5,Apple AirPods Gen 5 with Wireless Charging" data-widget-title="Today's best AirPods deals"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/airpods/airpods-5-vs-airpods-4-all-the-key-differences</link>
                                                                            <description>
                            <![CDATA[ The next Apple AirPods offers personalized spatial audio, a more comfortable fit and live translation, but other changes are harder to spot. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">s2kG6iXkaDKygtWfwUmZne</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/TWz5p3f2A62hD4eWjBgvBa-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 12 Sep 2026 04:45:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Airpods]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></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.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/TWz5p3f2A62hD4eWjBgvBa-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide / Apple]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The AirPods 5 (on left) vs the AirPods 4 (on right).]]></media:description>                                                            <media:text><![CDATA[The AirPods 5 (on left) vs the AirPods 4 (on right).]]></media:text>
                                <media:title type="plain"><![CDATA[The AirPods 5 (on left) vs the AirPods 4 (on right).]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/TWz5p3f2A62hD4eWjBgvBa-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>After weeks of rumors, Apple finally announced the <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-5-officially-announced-here-are-the-upgrades">AirPods 5</a> on September 9 at its "Surprise and Shine" <a href="https://www.tomsguide.com/phones/iphones/iphone-duo-hands-on-apple-nailed-it-and-just-put-everyone-else-on-notice">iPhone Duo</a> event. </p><p>Like the AirPods 4 before them, the AirPods 5 will come in two distinct flavors: a base model with ANC and a higher-end model with a wireless charging case and a volume slider on the stem.</p><p>Both versions of the AirPods 5 will be powered by the H2 Wireless Chip that offers personalized spatial audio and live translation, but is all that reason enough to upgrade from the now two-year-old AirPods 4? The answer isn't exactly clear cut.</p><div class="product"><a data-dimension112="cfca91f8-ae20-11f1-b9ba-e95f4b5bdff3" data-action="Deal Block" data-label="AirPods 5" data-dimension48="AirPods 5" data-dimension25="$129" href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" 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:94.40%;"><img id="mJGKbeqw2FwvrHngHh7KUc" name="Apple AirPods 5 deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/mJGKbeqw2FwvrHngHh7KUc.jpg" mos="" align="middle" fullscreen="" width="1500" height="1416" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods 5 are available for pre-order at Apple today. They offer live translation, full support for Siri AI, and 50% more noise reduction than the previous generation. They're also slightly cheaper than their predecessor with the ANC model selling for $129 and the AirPods 5 with Wireless Charging Case for $149. You can get your AirPods 5 engraved and personalized for free when you preorder them. Plus, Apple is also giving you three months of Apple Music with your purchase. <a class="view-deal button" href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow" data-dimension112="cfca91f8-ae20-11f1-b9ba-e95f4b5bdff3" data-action="Deal Block" data-label="AirPods 5" data-dimension48="AirPods 5" data-dimension25="$129">View Deal</a></p></div><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-price-and-availability"><span>AirPods 5 vs AirPods 4: Price and availability</span></h3><p>Because there are two AirPods 5 models, there are two prices: $129 / AU$219 for the base model and $149 / AU$249 for the higher-end model. </p><p>The AirPods 4, now a few years old, are obviously less expensive. They start at just $99 for the base model. Interestingly, however, the more advanced AirPods 4 with ANC is still sitting at $149. That's going to make the decision pretty easy if you need ANC. </p><p>My favorite Apple earbuds are the <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review">AirPods Pro 3</a>, and those start at $249. It's a pretty big leap in price, but I think they're all-around a better option, especially when compared to the AirPods 4. </p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-design"><span>AirPods 5 vs AirPods 4: Design</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3667px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="UMXRo3jZ6wo7vr4PP9HA8Z" name="IMG_7864.jpg" alt="AirPods 4" src="https://cdn.mos.cms.futurecdn.net/UMXRo3jZ6wo7vr4PP9HA8Z.jpg" mos="" align="middle" fullscreen="" width="3667" height="2063" 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>Admittedly, there's not a lot of difference on the design front. Apple claims the AirPods 5 offer "an all-new multiport acoustic architecture" but you certainly wouldn't know it just by looking at them. </p><p>The major design difference between the AirPods 5 and AirPods 4 can be found on the stems of the newer model. It's so small you'll likely miss it, but if you look close you'll see a touch-capacitive divot that acts as a volume slider. It's a very minor detail that the AirPods 5 take from the more-expensive AirPods Pro 3. </p><p>Speaking of nice-to-have features, the AirPods 5 with Wireless Charging Case (the proper name of the more-advanced AirPods 5 model) do offer wireless charging (duh). You can use the same charger as the Apple Watch, Qi-compatible chargers or plain old USB-C if you don't already own a wireless charging mat.</p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-controls"><span>AirPods 5 vs AirPods 4: Controls</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4096px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rHV7NoWYrNE2zRY52gLYvS" name="AirPods 4" alt="The AirPods 4 being displayed in hand" src="https://cdn.mos.cms.futurecdn.net/rHV7NoWYrNE2zRY52gLYvS.jpg" mos="" align="middle" fullscreen="" width="4096" height="2304" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Alex Bracetti/Future)</span></figcaption></figure><p>Besides the aforementioned volume slider functionality, both the AirPods 5 and AirPods 4 use touch sensitive stems to control your music. </p><p>Both pairs of true wireless earbuds can differentiate between squeezes and taps, but it's up to you to learn which one does what. </p><p>I've never been a huge fan of touch technology in earbuds and prefer to use my voice or my phone to control playback, but it's nice to have the option in a pinch.</p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-sound-quality"><span>AirPods 5 vs AirPods 4: Sound quality</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2679px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qTWTqkcso8N3vy8W3VCoCV" name="IMG_7868.jpg" alt="AirPods 4" src="https://cdn.mos.cms.futurecdn.net/qTWTqkcso8N3vy8W3VCoCV.jpg" mos="" align="middle" fullscreen="" width="2679" height="1507" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Here's where things are going to get a bit murky. Apple claims that, due to its improved sonic architecture, the AirPods 5 are going to sound better than the AirPods 4. Sound, as we all know, is subjective, and I can't really comment on their sound quality until I've heard them myself. </p><p>So until then, let's hear from someone who's listened to both. </p><p>Kate Kozuch, the Head of Video here at Tom's Guide was in Cupertino this week to check them out. According to her, the "bass was a noticeable improvement" on the AirPods 5. That counts for a lot in a pair of open earbuds like the AirPods. </p><p>Still, Kate didn't feel like there was a big enough difference between the two to warrant an upgrade. "This is coming from an AirPods 4 user: I don't think anyone needs to upgrade from AirPods 4." </p><p>If you want the best sound quality in a pair of wireless earbuds, then, your best bet is still going to be the pricier AirPods Pro series. </p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-features"><span>AirPods 5 vs AirPods 4: Features</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4096px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AfpNLN9gCw8bQsGWjQyLaf" name="Apple AirPods 4" alt="The AirPods 4 connected to an iPhone 12" src="https://cdn.mos.cms.futurecdn.net/AfpNLN9gCw8bQsGWjQyLaf.jpg" mos="" align="middle" fullscreen="" width="4096" height="2304" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The AirPods 5 take some of the best features of the AirPods Pro (besides sound quality, obviously) and bring them down to a more affordable price. </p><p>Like the Pro, the AirPods 5 offer Spatial Audio support, live translation and "significantly improved" ANC. How significant? According to Apple, the AirPods 5 cancel out "50 percent more external noise compared to AirPods 4 with Active Noise Cancellation." So pretty big.</p><p>Now, keep in mind that these are open earbuds, so the ANC isn't going to be world-class here. I'd still expect a pair of Bose over-ears to do a better job than the AirPods 5, but hey, I'll take any improvement we can get.</p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-battery-life"><span>AirPods 5 vs AirPods 4: Battery life</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Jqh9spPaAZV8jiAkZ89XzK" name="AirPods 4 Tips - Lede (2)" alt="AirPods 4" src="https://cdn.mos.cms.futurecdn.net/Jqh9spPaAZV8jiAkZ89XzK.jpg" 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/ Alex Bracetti)</span></figcaption></figure><p>One of our biggest complaints against the AirPods 4 was that their battery life was a little lacking. You could get around 5 hours of listening with ANC off, and 4 hours when you turn it on. That's a lot less than the competition and it means having to constantly reach for a charger.</p><p>The base AirPods 5 don't seem to offer much of an improvement here, but the AirPods 5 with Wireless Charging Case offer an additional hour with ANC turned on thanks to a redesigned battery.</p><p>Will an extra hour make or break the experience? Probably not, especially when rival earbuds like the <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-1000xm6-review">Sony WF-1000XM6</a> offer around 8 hours with ANC turned on. </p><h3 class="article-body__section" id="section-airpods-5-vs-airpods-4-early-outlook"><span>AirPods 5 vs AirPods 4: Early outlook</span></h3><p>If you're just getting AirPods for the first time, the AirPods 5 seem to be worth the extra $30 to get the ones with wireless charging. </p><p>If you're someone who already owns a pair of AirPods 4, however, it's not so cut and dry. Spatial audio, the audio slider and live translation are all great, don't get me wrong, but I'm not sure I'd toss out my old earbuds just to tap into them, especially when key areas like battery life haven't been improved all that much.</p><p>There are also some really great AirPods alternatives out there and, if you don't mind spending more for them, the award-winning AirPods Pro Series that I use every single day are some of the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> on the market. If you can, skip the AirPods and go straight for the AirPods Pro, but if the budget is tight, the base AirPods 5 will get you half the performance for around $100.</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/phones/iphones/interview-apple-ceo-john-ternus-on-his-defining-moment-for-the-iphone-duo">I interviewed Apple CEO John Ternus on the making of the iPhone Duo — here's what made him say 'yes, that's it'</a></li><li><a href="https://www.tomsguide.com/ai/apple-intelligence/people-are-freaking-out-about-the-new-always-listening-apple-watches-heres-what-apple-ceo-john-ternus-told-us">People are freaking out about the new 'always-listening' Apple Watches — here's what Apple CEO John Ternus told us</a></li><li><a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-ultra-4-vs-garmin-fenix-9-pro-price-specs-and-features-compared">Apple Watch Ultra 4 vs Garmin Fenix 9 Pro: price, specs and features compared</a></li></ul><div data-widget-type="multimodelreview" data-model-name="Apple AirPods 4,Apple AirPods Gen 5,Apple AirPods Gen 5 with Wireless Charging" data-widget-title="Today's best AirPods deals"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Tribit XSound Go is a decent portable speaker that costs less than $40 — but you can get better value for money elsewhere ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Tribit has made some of the <a href="https://www.tomsguide.com/us/best-bluetooth-speakers,review-1982.html">best cheap portable speakers</a> in recent years, and I bet it was hoping for a homerun with the XSound Go. Sadly, it falls just short of greatness — but it’s not <em>all </em>bad. The XSound Go sports a humble, inconspicuous design and boasts up to 24 hours of battery life. Treble is vibrant, while vocals sound loud and clear, making the speaker good for acoustic tracks.</p><p>But the non-existent bass is a chink in the XSound Go’s armor. There’s no companion app or custom EQ to salvage it either, and the speaker isn’t dustproof. Oh, and it's prone to scratches and fingerprints. Even though it costs just $39, I think it’s worth spending a little extra on a more competent speaker, like the JBL Go 5.</p><p>For the complete breakdown, read my full Tribit XSound Go review.</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-specs"><span>Tribit XSound Go review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Tribit-XSound-Bluetooth-Speakers-Built/dp/B07594HZ6Y/" target="_blank" rel="nofollow">$39</a> / <a href="https://www.amazon.co.uk/Bluetooth-Speakers-Tribit-Portable-Waterproof/dp/B078S4P3J9/" target="_blank" rel="nofollow">£39</a></p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IPX7</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3 with SBC, 3.5mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multi-speaker connectivity</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Output power</strong></p></td><td  ><p>16W, 2x 40mm drivers, 1x passive radiator</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>80Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Range</strong></p></td><td  ><p>30.5m / 100ft</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>24 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>6.7 x 2.3 x 2.2 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>13.44oz</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black, Blue, Beige</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-tribit-xsound-go-review-price-availability"><span>Tribit XSound Go review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="mVjNpYnuNjJscWY48JmEAX" name="Tribit_XSound_Go_Speaker_004" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/mVjNpYnuNjJscWY48JmEAX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Tribit XSound Go first came out in August 2017, and since then, Tribit has released upgraded versions with internal hardware improvements. The 2026 version features Bluetooth 5.3 and USB-C connectivity. The price has remained the same, and the latest version costs just <a href="https://www.amazon.com/Tribit-XSound-Bluetooth-Speakers-Built/dp/B07594HZ6Y/" target="_blank" rel="nofollow">$39</a> / <a href="https://www.amazon.co.uk/Bluetooth-Speakers-Tribit-Portable-Waterproof/dp/B078S4P3J9/" target="_blank" rel="nofollow">£39 at Amazon</a>. We’ve also seen it drop to as low as $24 / £31 in the past.</p><p>This makes the XSound Go one of the cheapest Bluetooth speakers we’ve tested. It’s priced similarly to the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tribit-stormbox-mini-plus-review">Tribit StormBox Mini+</a> ($39) and the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tozo-pe1-review">Tozo PE1</a> ($36). It’s also cheaper than the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/8bitdo-retro-cube-2-review">8BitDo Retro Cube 2</a> ($49), the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/jbl-go-5-review">JBL Go 5</a> ($54), and the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/sodapop-bluetooth-speaker-review">Sodapop</a> ($68). There's also the pricier version of this speaker, the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tribit-xsound-plus-2-review">Tribit XSound Plus</a> ($69). </p><p>Pound for pound, I’d recommend the JBL Go 5 above all else — yes, including the XSound Go. Let me tell you why.</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-design"><span>Tribit XSound Go review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="irxxtWMpoy6Qsap8RtsTnW" name="Tribit_XSound_Go_Speaker_003" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/irxxtWMpoy6Qsap8RtsTnW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Tribit XSound Go sports a simple, humble, generic pill-shaped design. The speaker is available in black, blue and beige colors. I tested the black model and it looks… fine. It fits right into most spaces, and doesn’t look out of place on a coffee table. Branding is minimal, with ‘Tribit’ written across the front metal grille in off-white on the black version. If you want a more eye-catching speaker, the 8BitDo Retro Cube 2 takes the cake.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aJD5Y3VsakM7hRN6ohnawi" name="Tribit_XSound_Go" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/aJD5Y3VsakM7hRN6ohnawi.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 outer shell features a matte, rubberized coating that gives the XSound Go a soft-touch, grippy feel. Frustratingly, though, this coating is prone to picking up fingerprints, marks and smudges. After unboxing the XSound Go and playing around with it a bit, I noticed my fingerprints were all over it. I put the speaker in my backpack to take it home, and the speaker looked scuffed. Quite annoying. I’d recommend picking up the JBL Go 5 instead if cosmetic wear-and-tear bothers you.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KXWPZC8yHXFNCxgqDLaCbj" name="Tribit_XSound_Go_size comparison" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/KXWPZC8yHXFNCxgqDLaCbj.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Size comparison with Marshall Stockwell III (middle) and Tribit StormBox Mini+ (right). </span><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Aesthetics aside, the XSound Go is a highly portable speaker, measuring 6.7 x 2.3 x 2.2 inches and weighing 13.44oz. I’ve had no trouble carrying it in my day bag, and if you’re wearing a winter coat, you should be able to slip it into your pocket, too. The XSound Go is certainly more pocket-friendly than the bulbous Tribit StormBox Mini+.</p><h2 id="weatherproofing">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SKFMqvNV5tVEuwDegBSxgW" name="Tribit_XSound_Go_Speaker_008" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/SKFMqvNV5tVEuwDegBSxgW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 XSound Go is IPX7-rated which means it’s waterproof and can be submerged in 1m of water for up to 30 minutes. This means that you can use it poolside without any worries. That’s decent waterproofing but the XSound Go isn’t dustproof, so I’d advise against using it on dusty hikes or in the desert.</p><p>This is the same waterproofing as the Tribit StormBox Mini+ and the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tribit-stormbox-2-review">Tribit StormBox 2</a> ($67). If you want a dustproof speaker that can even withstand tumbles and falls, I’d recommend spending more on the IP68-rated JBL Go 5 or the IP67-rated <a href="https://www.tomsguide.com/audio/speakers/bluetooth-speakers/earfun-uboom-l-review">EarFun UBoom L</a> ($79).</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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Q3hZgKCQTkTTPdqwan2tdX" name="Tribit_XSound_Go_Speaker_011" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/Q3hZgKCQTkTTPdqwan2tdX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Controls on the XSound Go are extremely straightforward. The physical buttons are located on the top, and are self-explanatory. You can use these to play/pause music, initiate Bluetooth pairing, swap between input sources, and of course, power the speaker on and off. I found them responsive in my testing, and all my commands were registered instantaneously.</p><h2 id="connectivity">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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uA9gAqdNouagQ52eVC7ErW" name="Tribit_XSound_Go_Speaker_006" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/uA9gAqdNouagQ52eVC7ErW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 I mentioned earlier, the XSound Go utilizes Bluetooth 5.3 with support for the SBC codec. There’s no AAC, like on the Tribit StormBox Mini+, or LC3 like on the JBL Go 5. LC3 is a more advanced codec that streams high-quality sound at lower bitrates, making the JBL Go 5 the better choice. You get multipoint connectivity, at the very least, which enables you to connect up to two devices to the XSound Go at the same time.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JzKmSthZZo6TQVzkFDbDzW" name="Tribit_XSound_Go_Speaker_007" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/JzKmSthZZo6TQVzkFDbDzW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 buy two XSound Gos, you can pair them together to create stereo sound. It works exactly as it does on the Tribit StormBox Mini+ and Tribit XSound Plus. If you buy directly from Tribit, you can get two speakers for <a href="https://tribit.com/products/tribit-xsound-go-bluetooth-speakers" target="_blank" rel="nofollow">$71 / £71</a>, saving you $9 / £9.</p><p>There’s also a 3.5mm port located around the back and protected by a rubber flap, and this can be used for wired playback.</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-sound-quality"><span>Tribit XSound Go 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NoxpDdkWg3pyY7Jt4af8XX" name="Tribit_XSound_Go_Speaker_005" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/NoxpDdkWg3pyY7Jt4af8XX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Tribit XSound Go is fitted with two 40mm drivers and a passive radiator, and it’s got a total power output of 16W. That’s enough for listening in a small room, or at an intimate picnic or dinner party. Treble and mids are the XSound Go’s strong suits, and as I’ll discuss below, bass is severely lacking. To test the speaker, I listened to my favorite testing songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the<a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"> best music streaming service</a> for audiophiles.</p><p>Good things first. ‘<a href="https://open.qobuz.com/track/414669704" target="_blank" rel="nofollow">Open Arms</a>’ by Little Simz ft. DEELA proved to be a great track to test the XSound Go’s ability to handle treble. The Afro-tech infused track features fast-moving percussion layers, and the XSound Go handled the sharp snares and claps extremely well. Each element sounded clean, and didn’t muddle together into a hiss. The aggressive hi-hats that maintain the track’s fast-paced energy didn’t sound too harsh, and they didn’t pierce through the rest of the soundstage either. The treble didn’t sound fatiguing at all.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gA9zuxqES2J7Ha3G5KSLeX" name="Tribit_XSound_Go_Speaker_012" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/gA9zuxqES2J7Ha3G5KSLeX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I then listened to ‘<a href="https://open.qobuz.com/track/366101014" target="_blank" rel="nofollow">Ice In My OJ</a>’ by Hayley Williams, and was impressed with the speaker’s wide dynamic range. Hayley’s vocals sat front and center. Instead of becoming a messy blur with the frantic backing instrumentation, they sounded adequately separated. Her “I’m in a band” screams in the chorus didn’t sound harsh or painfully sibilant either.</p><p>Now let’s talk about the area the XSound Go really struggles in — and that’s bass. The speaker has a frequency response range of 80Hz to 20KHz, so it isn’t surprising that it fails to produce deep, rumbling sub-bass. But I felt like the easier bass was non-existent, too, which is disappointing. The distorted alternative R&B electronic bassline in ‘<a href="https://open.qobuz.com/album/st6i3130ycvnj" target="_blank" rel="nofollow">Baby Driver</a>’ by 070 Shake lacked the low-end impact and <em>oomph</em> that drives the song. Through the JBL Go 5, for instance, the bassline sounds powerful and meaty, but I could barely hear or feel it through the XSound Go.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="W6tw4AwAxmxTvakqYdnKFX" name="Tribit_XSound_Go_Speaker_013" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/W6tw4AwAxmxTvakqYdnKFX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Similarly, the propulsive and consistent bassline in ‘<a href="https://open.qobuz.com/track/80418830" target="_blank" rel="nofollow">One Inch Punch</a>’ by Yin Yin sounded like it was pushed into the background. It sounded muddy, suppressed and like it was bleeding into the lower-mids, rather than tight and controlled. The otherwise crisp, rhythmic definition was lost, and this made the track lose its agility and groove. It’s surprising that the XSound Go struggles so much, when its sibling, the Tribit StormBox Mini+, doesn’t.</p><p>If you want a speaker that doesn’t sacrifice any aspect of the soundstage, I really can’t recommend the JBL Go 5 enough. It breezes past the XSound Go with its overall refined sound.</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>, our favorite </em><a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"><em>music streaming service</em></a><em> for audiophiles.</em></p><iframe allow="" height="390" width="378" id="" style="" class="position-center" data-lazy-priority="high" data-lazy-src="https://widget.qobuz.com/playlist/69962266?zone=GB-en"></iframe><h2 class="article-body__section" id="section-tribit-xsound-go-review-app"><span>Tribit XSound Go review: App</span></h2><p>Though the Tribit app exists on iOS and Android, the XSound Go isn’t compatible with it. This means that you don’t get access to a custom equalizer or EQ presets. If you want a budget-friendly Bluetooth speaker with custom EQ, I’d recommend the Tribit StormBox Mini+, the JBL Go 5 or the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tribit-stormbox-micro-3-review">Tribit StormBox Micro 3</a> ($64).</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-battery-life"><span>Tribit XSound Go 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="surizBJjNFpZPbtS73e4vW" name="Tribit_XSound_Go_Speaker_009" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/surizBJjNFpZPbtS73e4vW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Tribit says the XSound Go can last up to 24 hours on a single charge and while playing on a “moderate” volume. This is the same as the pricier XSound Plus. Under typical use, the XSound Go should last you between 18 and 20 hours, according to the brand. To test the XSound Go’s battery life, I played music at 50% volume and after about 20 hours, I was prompted to charge it.</p><p>The XSound Go, then, trumps its competition when it comes to sheer mileage. In our testing, the JBL Go 5 lasted nine hours; the Tribit StormBox Mini+ died after 13 hours; and the Tozo PE1 netted 15 hours of playback time. The 8BitDo Retro Cube 2 outperforms the XSound Go with its 30-hour battery life, but you’d be sacrificing waterproofing if you went with the 8BitDo.</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-verdict"><span>Tribit XSound Go review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="H3PNA5TUxAUb7zNyMz8vNX" name="Tribit_XSound_Go_Speaker_002" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/H3PNA5TUxAUb7zNyMz8vNX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Tribit XSound Go isn’t the worst speaker I’ve tested… but it’s also not the best. Don’t get me wrong: the speaker gets a few things right. The inconspicuous design helps it fit into most spaces, and it packs up to 24 hours of playtime. It doesn’t make treble sound fatiguing, and vocal-heavy tracks are a piece of cake for the XSound Go.</p><p>But the XSound Go sacrifices the bass. Lows feel non-existent or like they’ve been pushed into the background. Annoyingly, there’s no custom equalizer (as there’s no companion app) to fix it. The speaker isn’t dustproof either, when many of its competitors are, and it’s a fingerprint magnet.</p><p>If you want a better, fuller sound, I’d recommend its sibling, the Tribit StormBox Mini+. If you can swing a few extra dollars, the JBL Go 5 is a no-brainer as it’s better in nearly every aspect.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/bluetooth-speakers/tribit-xsound-go-review</link>
                                                                            <description>
                            <![CDATA[ The Tribit XSound Go is a decent portable speaker with crisp treble and IPX7 waterproofing, but it lacks punchy bass, custom EQ, and is prone to scratches. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">8zD5AF6FYhC8Rk9X6Bze6U</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/mVjNpYnuNjJscWY48JmEAX-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 11 Sep 2026 12:23:26 +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.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/mVjNpYnuNjJscWY48JmEAX-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A black Tribit XSound Go portable Bluetooth speaker]]></media:description>                                                            <media:text><![CDATA[A black Tribit XSound Go portable Bluetooth speaker]]></media:text>
                                <media:title type="plain"><![CDATA[A black Tribit XSound Go portable Bluetooth speaker]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/mVjNpYnuNjJscWY48JmEAX-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Tribit has made some of the <a href="https://www.tomsguide.com/us/best-bluetooth-speakers,review-1982.html">best cheap portable speakers</a> in recent years, and I bet it was hoping for a homerun with the XSound Go. Sadly, it falls just short of greatness — but it’s not <em>all </em>bad. The XSound Go sports a humble, inconspicuous design and boasts up to 24 hours of battery life. Treble is vibrant, while vocals sound loud and clear, making the speaker good for acoustic tracks.</p><p>But the non-existent bass is a chink in the XSound Go’s armor. There’s no companion app or custom EQ to salvage it either, and the speaker isn’t dustproof. Oh, and it's prone to scratches and fingerprints. Even though it costs just $39, I think it’s worth spending a little extra on a more competent speaker, like the JBL Go 5.</p><p>For the complete breakdown, read my full Tribit XSound Go review.</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-specs"><span>Tribit XSound Go review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Tribit-XSound-Bluetooth-Speakers-Built/dp/B07594HZ6Y/" target="_blank" rel="nofollow">$39</a> / <a href="https://www.amazon.co.uk/Bluetooth-Speakers-Tribit-Portable-Waterproof/dp/B078S4P3J9/" target="_blank" rel="nofollow">£39</a></p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IPX7</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3 with SBC, 3.5mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multi-speaker connectivity</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Output power</strong></p></td><td  ><p>16W, 2x 40mm drivers, 1x passive radiator</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>80Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Range</strong></p></td><td  ><p>30.5m / 100ft</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>24 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>6.7 x 2.3 x 2.2 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>13.44oz</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black, Blue, Beige</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-tribit-xsound-go-review-price-availability"><span>Tribit XSound Go review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="mVjNpYnuNjJscWY48JmEAX" name="Tribit_XSound_Go_Speaker_004" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/mVjNpYnuNjJscWY48JmEAX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Tribit XSound Go first came out in August 2017, and since then, Tribit has released upgraded versions with internal hardware improvements. The 2026 version features Bluetooth 5.3 and USB-C connectivity. The price has remained the same, and the latest version costs just <a href="https://www.amazon.com/Tribit-XSound-Bluetooth-Speakers-Built/dp/B07594HZ6Y/" target="_blank" rel="nofollow">$39</a> / <a href="https://www.amazon.co.uk/Bluetooth-Speakers-Tribit-Portable-Waterproof/dp/B078S4P3J9/" target="_blank" rel="nofollow">£39 at Amazon</a>. We’ve also seen it drop to as low as $24 / £31 in the past.</p><p>This makes the XSound Go one of the cheapest Bluetooth speakers we’ve tested. It’s priced similarly to the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tribit-stormbox-mini-plus-review">Tribit StormBox Mini+</a> ($39) and the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tozo-pe1-review">Tozo PE1</a> ($36). It’s also cheaper than the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/8bitdo-retro-cube-2-review">8BitDo Retro Cube 2</a> ($49), the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/jbl-go-5-review">JBL Go 5</a> ($54), and the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/sodapop-bluetooth-speaker-review">Sodapop</a> ($68). There's also the pricier version of this speaker, the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tribit-xsound-plus-2-review">Tribit XSound Plus</a> ($69). </p><p>Pound for pound, I’d recommend the JBL Go 5 above all else — yes, including the XSound Go. Let me tell you why.</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-design"><span>Tribit XSound Go review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="irxxtWMpoy6Qsap8RtsTnW" name="Tribit_XSound_Go_Speaker_003" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/irxxtWMpoy6Qsap8RtsTnW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Tribit XSound Go sports a simple, humble, generic pill-shaped design. The speaker is available in black, blue and beige colors. I tested the black model and it looks… fine. It fits right into most spaces, and doesn’t look out of place on a coffee table. Branding is minimal, with ‘Tribit’ written across the front metal grille in off-white on the black version. If you want a more eye-catching speaker, the 8BitDo Retro Cube 2 takes the cake.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aJD5Y3VsakM7hRN6ohnawi" name="Tribit_XSound_Go" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/aJD5Y3VsakM7hRN6ohnawi.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 outer shell features a matte, rubberized coating that gives the XSound Go a soft-touch, grippy feel. Frustratingly, though, this coating is prone to picking up fingerprints, marks and smudges. After unboxing the XSound Go and playing around with it a bit, I noticed my fingerprints were all over it. I put the speaker in my backpack to take it home, and the speaker looked scuffed. Quite annoying. I’d recommend picking up the JBL Go 5 instead if cosmetic wear-and-tear bothers you.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KXWPZC8yHXFNCxgqDLaCbj" name="Tribit_XSound_Go_size comparison" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/KXWPZC8yHXFNCxgqDLaCbj.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Size comparison with Marshall Stockwell III (middle) and Tribit StormBox Mini+ (right). </span><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Aesthetics aside, the XSound Go is a highly portable speaker, measuring 6.7 x 2.3 x 2.2 inches and weighing 13.44oz. I’ve had no trouble carrying it in my day bag, and if you’re wearing a winter coat, you should be able to slip it into your pocket, too. The XSound Go is certainly more pocket-friendly than the bulbous Tribit StormBox Mini+.</p><h2 id="weatherproofing">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SKFMqvNV5tVEuwDegBSxgW" name="Tribit_XSound_Go_Speaker_008" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/SKFMqvNV5tVEuwDegBSxgW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 XSound Go is IPX7-rated which means it’s waterproof and can be submerged in 1m of water for up to 30 minutes. This means that you can use it poolside without any worries. That’s decent waterproofing but the XSound Go isn’t dustproof, so I’d advise against using it on dusty hikes or in the desert.</p><p>This is the same waterproofing as the Tribit StormBox Mini+ and the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tribit-stormbox-2-review">Tribit StormBox 2</a> ($67). If you want a dustproof speaker that can even withstand tumbles and falls, I’d recommend spending more on the IP68-rated JBL Go 5 or the IP67-rated <a href="https://www.tomsguide.com/audio/speakers/bluetooth-speakers/earfun-uboom-l-review">EarFun UBoom L</a> ($79).</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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Q3hZgKCQTkTTPdqwan2tdX" name="Tribit_XSound_Go_Speaker_011" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/Q3hZgKCQTkTTPdqwan2tdX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Controls on the XSound Go are extremely straightforward. The physical buttons are located on the top, and are self-explanatory. You can use these to play/pause music, initiate Bluetooth pairing, swap between input sources, and of course, power the speaker on and off. I found them responsive in my testing, and all my commands were registered instantaneously.</p><h2 id="connectivity">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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uA9gAqdNouagQ52eVC7ErW" name="Tribit_XSound_Go_Speaker_006" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/uA9gAqdNouagQ52eVC7ErW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 I mentioned earlier, the XSound Go utilizes Bluetooth 5.3 with support for the SBC codec. There’s no AAC, like on the Tribit StormBox Mini+, or LC3 like on the JBL Go 5. LC3 is a more advanced codec that streams high-quality sound at lower bitrates, making the JBL Go 5 the better choice. You get multipoint connectivity, at the very least, which enables you to connect up to two devices to the XSound Go at the same time.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JzKmSthZZo6TQVzkFDbDzW" name="Tribit_XSound_Go_Speaker_007" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/JzKmSthZZo6TQVzkFDbDzW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 buy two XSound Gos, you can pair them together to create stereo sound. It works exactly as it does on the Tribit StormBox Mini+ and Tribit XSound Plus. If you buy directly from Tribit, you can get two speakers for <a href="https://tribit.com/products/tribit-xsound-go-bluetooth-speakers" target="_blank" rel="nofollow">$71 / £71</a>, saving you $9 / £9.</p><p>There’s also a 3.5mm port located around the back and protected by a rubber flap, and this can be used for wired playback.</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-sound-quality"><span>Tribit XSound Go 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NoxpDdkWg3pyY7Jt4af8XX" name="Tribit_XSound_Go_Speaker_005" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/NoxpDdkWg3pyY7Jt4af8XX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Tribit XSound Go is fitted with two 40mm drivers and a passive radiator, and it’s got a total power output of 16W. That’s enough for listening in a small room, or at an intimate picnic or dinner party. Treble and mids are the XSound Go’s strong suits, and as I’ll discuss below, bass is severely lacking. To test the speaker, I listened to my favorite testing songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the<a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"> best music streaming service</a> for audiophiles.</p><p>Good things first. ‘<a href="https://open.qobuz.com/track/414669704" target="_blank" rel="nofollow">Open Arms</a>’ by Little Simz ft. DEELA proved to be a great track to test the XSound Go’s ability to handle treble. The Afro-tech infused track features fast-moving percussion layers, and the XSound Go handled the sharp snares and claps extremely well. Each element sounded clean, and didn’t muddle together into a hiss. The aggressive hi-hats that maintain the track’s fast-paced energy didn’t sound too harsh, and they didn’t pierce through the rest of the soundstage either. The treble didn’t sound fatiguing at all.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gA9zuxqES2J7Ha3G5KSLeX" name="Tribit_XSound_Go_Speaker_012" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/gA9zuxqES2J7Ha3G5KSLeX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I then listened to ‘<a href="https://open.qobuz.com/track/366101014" target="_blank" rel="nofollow">Ice In My OJ</a>’ by Hayley Williams, and was impressed with the speaker’s wide dynamic range. Hayley’s vocals sat front and center. Instead of becoming a messy blur with the frantic backing instrumentation, they sounded adequately separated. Her “I’m in a band” screams in the chorus didn’t sound harsh or painfully sibilant either.</p><p>Now let’s talk about the area the XSound Go really struggles in — and that’s bass. The speaker has a frequency response range of 80Hz to 20KHz, so it isn’t surprising that it fails to produce deep, rumbling sub-bass. But I felt like the easier bass was non-existent, too, which is disappointing. The distorted alternative R&B electronic bassline in ‘<a href="https://open.qobuz.com/album/st6i3130ycvnj" target="_blank" rel="nofollow">Baby Driver</a>’ by 070 Shake lacked the low-end impact and <em>oomph</em> that drives the song. Through the JBL Go 5, for instance, the bassline sounds powerful and meaty, but I could barely hear or feel it through the XSound Go.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="W6tw4AwAxmxTvakqYdnKFX" name="Tribit_XSound_Go_Speaker_013" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/W6tw4AwAxmxTvakqYdnKFX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Similarly, the propulsive and consistent bassline in ‘<a href="https://open.qobuz.com/track/80418830" target="_blank" rel="nofollow">One Inch Punch</a>’ by Yin Yin sounded like it was pushed into the background. It sounded muddy, suppressed and like it was bleeding into the lower-mids, rather than tight and controlled. The otherwise crisp, rhythmic definition was lost, and this made the track lose its agility and groove. It’s surprising that the XSound Go struggles so much, when its sibling, the Tribit StormBox Mini+, doesn’t.</p><p>If you want a speaker that doesn’t sacrifice any aspect of the soundstage, I really can’t recommend the JBL Go 5 enough. It breezes past the XSound Go with its overall refined sound.</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>, our favorite </em><a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"><em>music streaming service</em></a><em> for audiophiles.</em></p><iframe allow="" height="390" width="378" id="" style="" class="position-center" data-lazy-priority="high" data-lazy-src="https://widget.qobuz.com/playlist/69962266?zone=GB-en"></iframe><h2 class="article-body__section" id="section-tribit-xsound-go-review-app"><span>Tribit XSound Go review: App</span></h2><p>Though the Tribit app exists on iOS and Android, the XSound Go isn’t compatible with it. This means that you don’t get access to a custom equalizer or EQ presets. If you want a budget-friendly Bluetooth speaker with custom EQ, I’d recommend the Tribit StormBox Mini+, the JBL Go 5 or the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/tribit-stormbox-micro-3-review">Tribit StormBox Micro 3</a> ($64).</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-battery-life"><span>Tribit XSound Go 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="surizBJjNFpZPbtS73e4vW" name="Tribit_XSound_Go_Speaker_009" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/surizBJjNFpZPbtS73e4vW.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Tribit says the XSound Go can last up to 24 hours on a single charge and while playing on a “moderate” volume. This is the same as the pricier XSound Plus. Under typical use, the XSound Go should last you between 18 and 20 hours, according to the brand. To test the XSound Go’s battery life, I played music at 50% volume and after about 20 hours, I was prompted to charge it.</p><p>The XSound Go, then, trumps its competition when it comes to sheer mileage. In our testing, the JBL Go 5 lasted nine hours; the Tribit StormBox Mini+ died after 13 hours; and the Tozo PE1 netted 15 hours of playback time. The 8BitDo Retro Cube 2 outperforms the XSound Go with its 30-hour battery life, but you’d be sacrificing waterproofing if you went with the 8BitDo.</p><h2 class="article-body__section" id="section-tribit-xsound-go-review-verdict"><span>Tribit XSound Go review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="H3PNA5TUxAUb7zNyMz8vNX" name="Tribit_XSound_Go_Speaker_002" alt="A black Tribit XSound Go portable Bluetooth speaker" src="https://cdn.mos.cms.futurecdn.net/H3PNA5TUxAUb7zNyMz8vNX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Tribit XSound Go isn’t the worst speaker I’ve tested… but it’s also not the best. Don’t get me wrong: the speaker gets a few things right. The inconspicuous design helps it fit into most spaces, and it packs up to 24 hours of playtime. It doesn’t make treble sound fatiguing, and vocal-heavy tracks are a piece of cake for the XSound Go.</p><p>But the XSound Go sacrifices the bass. Lows feel non-existent or like they’ve been pushed into the background. Annoyingly, there’s no custom equalizer (as there’s no companion app) to fix it. The speaker isn’t dustproof either, when many of its competitors are, and it’s a fingerprint magnet.</p><p>If you want a better, fuller sound, I’d recommend its sibling, the Tribit StormBox Mini+. If you can swing a few extra dollars, the JBL Go 5 is a no-brainer as it’s better in nearly every aspect.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve been testing Beyerdynamic’s new affordable headphones with effective ANC and 90 hours of battery — but I’m not entirely sold on them ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The <a href="https://www.tomsguide.com/best-picks/best-over-ear-headphones">best over-ear headphones</a> don’t need to break the bank, and the Beyerdynamic Aventho Y almost prove that. Affordable at just $149, the Aventho Y boast effective active noise cancelation both indoors and outdoors. They come in a few fun colors, and they’re extremely comfortable for all-day wear. Promising up to 90 hours of stamina, the Aventho Y deliver on almost every front…. except for balanced sound.</p><p>Don’t get me wrong: the Aventho Y make vocals sound strong and clear, but the rest of the soundstage, not so much. It lacks openness and sounds mostly flat. You’ll need the companion app to customize the equalizer and salvage it. Call quality isn’t great either, as the headphones make you sound scratchy.</p><p>Should you still buy these cans? Read my full Beyerdynamic Aventho Y review to find out.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-specs"><span>Beyerdynamic Aventho Y 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/beyerdynamic-AVENTHO-Wireless-Bluetooth-Headphones/dp/B0H7R8BJH3/" target="_blank" rel="nofollow">$149</a> / <a href="https://polar.uk.com/beyerdynamic-aventho-y-wireless-headphones-liquid-black" target="_blank" rel="nofollow">£129</a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Closed back</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>45mm dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Impedance</strong></p></td><td  ><p>18Ω</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP53</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 6.0 with SBC and AAC, USB-C wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></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></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>90 hours (ANC off) | 60 hours (ANC on)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>11.14 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Blue Fusion, Liquid Black, Peach Blush, Soft Jade</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-price-availability"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YgM9wVLazqK9nT93r4Mzgc" name="Bayerdynamic_Aventho_Y_Headphones_016" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/YgM9wVLazqK9nT93r4Mzgc.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Announced at IFA in September 2026, the Beyerdynamic Aventho Y are the latest over-ears to join the brand’s extensive lineup of consumer headphones. Priced at <a href="https://www.amazon.com/beyerdynamic-AVENTHO-Wireless-Bluetooth-Headphones/dp/B0H7R8BJH3/" target="_blank" rel="nofollow">$149 at Amazon U.S.</a> / <a href="https://polar.uk.com/beyerdynamic-aventho-y-wireless-headphones-liquid-black" target="_blank" rel="nofollow">£129 at Polar U.K.</a>, the Aventho Y are Beyerdynamic’s cheapest lifestyle headphones, at the time of writing. The cans come in Blue Fusion, Liquid Black, Peach Blush, and Soft Jade colors.</p><p>The Aventho Y offer ANC, long battery life with user-replaceable battery packs, and are extremely comfortable. These are some of the cheapest mid-range ANC headphones we’ve tested, and they’re cheaper than the <a href="https://www.tomsguide.com/audio/headphones/sony-wh-1000xm4c-wh-ch730n-wh-ch530-announced">Sony WH-CH730N</a> ($179), the <a href="https://www.tomsguide.com/reviews/sennheiser-accentum">Sennheiser Accentum</a> ($179), the <a href="https://www.tomsguide.com/audio/headphones/jbl-live-770nc-review">JBL Live 770NC</a> ($199), and the <a href="https://www.tomsguide.com/audio/headphones/beats-solo-4-review">Beats Solo 4</a> ($199).</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-design"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9V7YAqHtxZgJeYfXvCyo4d" name="Bayerdynamic_Aventho_Y_Headphones_015" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/9V7YAqHtxZgJeYfXvCyo4d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Beyerdynamic Aventho Y make a strong first impression with their striking colors. The Peach Blush model I tested is certainly a looker, with light and airy accents that make them stand out in a market crowded with black and dark colors. The stylized “Y” engraved into each (large) earcup is a nice touch, and the subtle Beyerdynamic branding on the headband doesn’t draw much attention either.</p><p>The Aventho Y weigh just 11.14 ounces and even though they’re slightly heavier than my trusty <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review">Marshall Monitor III</a> (8.8 ounces, $349), the difference isn’t that noticeable. They’re also heavier than their siblings, the <a href="https://www.tomsguide.com/audio/headphones/beyerdynamic-aventho-100-review">Aventho 100</a> (7.76 ounces, $229), but the added weight is a decent trade-off if you want a user-replaceable battery and a more comfortable fit.</p><p>For easy transport, the Aventho Y are foldable so you can save some space in your day bag. A drawstring cloth bag is included in the box, too.</p><h2 id="weatherproofing-2">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="A7PV2SXGV73zp9kUq4qA3d" name="Bayerdynamic_Aventho_Y_Headphones_014" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/A7PV2SXGV73zp9kUq4qA3d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 normal for over-ear headphones to not have official IP waterproof ratings, but the Aventho Y do. These cans are IP53-rated, just like the Aventho 100, so they’re protected against dust and light water splashes. You should be just fine wearing them to the gym. In contrast, the JBL Live 770NC and the Sony WH-CH730N aren’t waterproof at all — JBL and Sony don’t specify that they are, at least.</p><h2 id="comfort">Comfort</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="T4NMDWxrsvXDx6rAa9g64d" name="Bayerdynamic_Aventho_Y_Headphones_011" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/T4NMDWxrsvXDx6rAa9g64d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Aventho Y are extremely comfortable headphones, and that’s thanks to their large, spacious earcups and cushioned headband. The ear cushions feature soft foam interiors, and they’re covered in soft leatherette. These ear cushions are magnetic so they can be twisted off and replaced if they get damaged.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5v4CkXFiEch9ZXiGUMG44d" name="Bayerdynamic_Aventho_Y_Headphones_010" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/5v4CkXFiEch9ZXiGUMG44d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Similarly, the headband’s metal frame uses the same leatherette cushioning for added comfort, and it relieves the pressure you’d otherwise feel on the top of your head. Having worn the Aventho Y for a six-hour stint, I can confirm that they’re suitable for all-day wear, as my ears didn’t overheat or feel sweaty.</p><h2 id="controls-2">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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nLeBCaiHpDhDsVkU6x97nb" name="Bayerdynamic_Aventho_Y_Headphones_006" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/nLeBCaiHpDhDsVkU6x97nb.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Sporting a straightforward control scheme, everything, from volume to ANC, is adjusted via physical buttons and toggles on the Aventho Y’s earcups. You can increase or decrease the volume via the buttons on the right earcup. Located between these is a custom button which can be customized via the app to change tracks, enable voice assistant, and more. Controls for ANC and Bluetooth are on the left earcup. They’re all super straightforward to get a hang of.</p><h2 id="connectivity-2">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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pWp8DbjTzQzLLhixrnA3Gc" name="Bayerdynamic_Aventho_Y_Headphones_002" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/pWp8DbjTzQzLLhixrnA3Gc.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Aventho Y utilize Bluetooth 6.0 with support for SBC and AAC codecs. However, there’s no support for LC3, LDAC or aptX Lossless, and you’ll need to spend more on the Sennheiser Accentum if you want aptX. The lack of advanced codecs isn’t uncommon in this price range, though, as neither the JBL Live 770NC nor the Beats Solo 4 support them.</p><p>For those who want more versatile wireless connectivity, Beyerdynamic is planning on selling an optional 2.4GHz dongle (which can be handily stored inside one of the earcups) along with a detachable boom mic come October. Beyerdynamic is yet to officially announce sample rates for the dongle, but I’ve reached out to a representative to find out more. You can also use a USB-C cable for wired playback.</p><p>They also feature multipoint connectivity, enabling you to connect up to two devices simultaneously — I did so with my <a href="https://www.tomsguide.com/reviews/macbook-air-2022-m2">MacBook Air M2</a> and <a href="https://www.tomsguide.com/phones/google-pixel-phones/google-pixel-10-pro-xl-review">Google Pixel 10 Pro XL</a>. The Aventho Y say “Multipoint” out loud to you when two devices are connected at once. You also get Google Fast Pair and as soon as I put the headphones into pairing mode, my Pixel recognized and connected to them immediately.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-sound-quality"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vLyobMN2AG3DgE7EuznF6c" name="Bayerdynamic_Aventho_Y_Headphones_003" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/vLyobMN2AG3DgE7EuznF6c.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Beyerdynamic Aventho Y ulitize a 45mm driver to cover a frequency response range of 20Hz to 20KHz (so the entirety of the average human hearing spectrum). To test the headphones, I listened to my array of testing songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the<a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"> best music streaming service</a> for audiophiles — and I was left with mixed feelings.</p><p>Vocals are the Aventho Y’s strong suit. In ‘<a href="https://open.qobuz.com/track/313214062" target="_blank" rel="nofollow">Cheers to Me</a>’ by Self Esteem, Rebecca Lucy Taylor’s vocals felt raw and intimate. Each word sounded textured, every consonant felt sharp, and plosives never popped or bled into one another. The backing vocals panned wide to the far left and right, creating an immersive listening experience. As all the voices came together in the bridge, they sounded harmonious and powerful.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TopxQp9fQ7fdvzR38diX4d" name="Bayerdynamic_Aventho_Y_Headphones_013" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/TopxQp9fQ7fdvzR38diX4d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 unfortunately, the Aventho Y struggle with almost every other aspect of the soundstage. Without any EQ adjustments and straight out of the box, the Aventho Y sound boxy, hollow and lack openness. The dark pulsating bassline that drives ‘<a href="https://open.qobuz.com/album/xl54kemeg6wws" target="_blank" rel="nofollow">2515</a>’ by Wasia Project sounded like someone had turned the bass off. The song sounded closed-in and hollow, and it felt like it had lost its three-dimensionality. While the vocals still sounded defined, the rest of the track didn’t.</p><p>I then listened to ‘<a href="https://open.qobuz.com/track/80418830" target="_blank" rel="nofollow">One Inch Punch</a>’ by Yin Yin to see if the treble sounded just as flat as the bass. While the electric guitar solos shimmered, they didn’t possess the same clarity and vibrancy you get when listening to the song through a more capable set of cans. It also felt like all the instruments were pushed into the background. The bassline sounded dull as did the drums. Heavy tracks like ‘<a href="https://open.qobuz.com/album/qebcwambt9mpa" target="_blank" rel="nofollow">Put Me in a Padded Room</a>’ by Icon For Hire sounded muddled and like the headphones were easily overwhelmed.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ocA8nhQ3CgpL4YpEirNq3d" name="Bayerdynamic_Aventho_Y_Headphones_008" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/ocA8nhQ3CgpL4YpEirNq3d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Luckily, though, I was able to salvage a lot of the sound quality by adjusting the Aventho Y’s equalizer through the companion app (more on that shortly). I fixed a lot of the issues by simply playing around with the 10-band EQ and finding my ideal sound. It’s great to have such extensive customization at your fingertips, but I wish the Aventho Y sounded balanced straight out of the box — like the Sennheiser Accentum or the JBL Live 770NC.</p><p><em>Listen along to my review with the Beyerdynamic Aventho Y testing playlist:</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/69742399?zone=GB-en"></iframe><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-app"><span>Beyerdynamic Aventho Y review: App</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1510px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="gRcWZ9eDJDPYmFNCvE2Nk6" name="Aventho_Y_app" alt="Beyerdynamic app screenshots" src="https://cdn.mos.cms.futurecdn.net/gRcWZ9eDJDPYmFNCvE2Nk6.jpg" mos="" align="middle" fullscreen="1" width="1510" height="850" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/gRcWZ9eDJDPYmFNCvE2Nk6.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Beyerdynamic)</span></figcaption></figure><p>The Aventho Y are accompanied by the Beyerdynamic app, available for free on <a href="https://apps.apple.com/us/app/beyerdynamic/id6477286323" target="_blank" rel="nofollow">iOS</a> and <a href="https://play.google.com/store/apps/details?id=com.beyerdynamic.app&hl=us" target="_blank" rel="nofollow">Android</a>. It enables you to customize the controls, adjust the level of ANC, enable Low Latency mode to minimize audio delays, choose an EQ preset, and customize the 10-band eqaulizer to your liking.</p><p>Beyerdynamic’s app is easy to use and it sports a clean interface without any intrusive pop-ups. That’s good because given that you need the app to fix the Aventho Y’s sound, I’d say it’s a necessity and not an optional download.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-anc"><span>Beyerdynamic Aventho Y review: 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5c7V7sbRz2qHnuFUfjmttb" name="Bayerdynamic_Aventho_Y_Headphones_007" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/5c7V7sbRz2qHnuFUfjmttb.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Beyerdynamic Aventho Y feature effective active noise cancelation that works well indoors and outdoors. Indoors, I wasn’t able to hear the kettle boiling, the clicking-clacking of <a href="https://www.tomsguide.com/best-picks/best-mechanical-keyboards">mechanical keyboards</a>, or other people talking. My partner had to wave several times to get my attention when I was testing the Aventho Y.</p><p>On a busy bus back from work, the Aventho Y managed to drown out the sound of people talking loudly. While I could hear the sound of the bus rattling, it wasn’t intrusive or too much, and the Aventho Y did a good job of ensuring I felt immersed in my tunes.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-call-quality"><span>Beyerdynamic Aventho Y review: 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2rSh22ao74zeJmhAS7S63d" name="Bayerdynamic_Aventho_Y_Headphones_012" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/2rSh22ao74zeJmhAS7S63d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Unfortunately, the Beyerdynamic Aventho Y’s call quality is quite disappointing. On a Google Meet team call, my colleagues winced when I started speaking — and they’ve heard me talk before so I know it isn’t <em>me. </em>I asked them how I sounded, and they noted that my voice was scratchy, distant, and “not great at all.”</p><p>To rule out the possibility that this was down to an unstable internet connection, I called my partner who said that my voice sounded muffled and she couldn’t understand my words very clearly. I’d recommend the Sennheiser Accentum if you want better call quality.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-battery-life"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bCuh4rrDJR3ggxnwVrNESc" name="Bayerdynamic_Aventho_Y_Headphones_004" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/bCuh4rrDJR3ggxnwVrNESc.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>An area the Beyerdynamic Aventho Y perform formidably is battery life. Beyerdynamic says that on a single charge, the Aventho Y can last up to 90 hours with ANC off, or 60 hours with ANC on. This is excellent, and the headphones outclass the Sennheiser Accentum (50 hours), the Sony WH-CH730N (50 hours) and the JBL Tune 770NC (44 hours) when ANC is enabled.</p><p>With ANC enabled and music playing at 50% volume, I left the Aventho Y running over a few nights. After approximately 50 hours, I had just 5% battery remaining. Beyerdynamic’s battery claims are, then, fairly reliable.</p><p>The Aventho Y feature a user-replaceable battery, which increases the longevity of the headphones. The manual specifies how to remove the battery carefully and safely, should you need to do so.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-verdict"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="MeB7TVLnzKUXmG7wL5FW5c" name="Bayerdynamic_Aventho_Y_Headphones_009" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/MeB7TVLnzKUXmG7wL5FW5c.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Beyerdynamic Aventho Y check almost every box. They boast up to 90 hours of battery life, effective ANC indoors and outdoors, a comfortable fit, a colorful design, and they come with a user-friendly companion app. Vocals are clear, strong and nuanced, yes, but everything else sounds flat — and that’s no bueno for a pair of headphones.</p><p>While the sound quality is salvageable thanks to the custom 10-band equalizer, it’s a shame that the Aventho Y don’t sound great straight out of the box. The bass sounds muddy and the treble struggles to shine. Call quality isn’t great either as several people noted that I sounded scratchy on their end.</p><p>Though the Aventho Y are fun-looking, comfortable and last a long time, it’s worth spending a little more on the likes of the JBL Live 770NC if you want solid sound as soon as you unbox the cans. At the end of the day, the Aventho Y deliver on some fronts but fall flat where it matters most.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/over-ear-headphones/beyerdynamic-aventho-y-review</link>
                                                                            <description>
                            <![CDATA[ The Beyerdynamic Aventho Y are comfortable over-ear headphones with effective ANC and long battery life, but they sound boxy and flat straight out of the box. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">G8iaHK39teEcsx6VegGNG9</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/YgM9wVLazqK9nT93r4Mzgc-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 11 Sep 2026 12:07:16 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Over-Ear Headphones]]></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.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/YgM9wVLazqK9nT93r4Mzgc-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Beyerdynamic Aventho Y over-ear headphones]]></media:description>                                                            <media:text><![CDATA[Beyerdynamic Aventho Y over-ear headphones]]></media:text>
                                <media:title type="plain"><![CDATA[Beyerdynamic Aventho Y over-ear headphones]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/YgM9wVLazqK9nT93r4Mzgc-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The <a href="https://www.tomsguide.com/best-picks/best-over-ear-headphones">best over-ear headphones</a> don’t need to break the bank, and the Beyerdynamic Aventho Y almost prove that. Affordable at just $149, the Aventho Y boast effective active noise cancelation both indoors and outdoors. They come in a few fun colors, and they’re extremely comfortable for all-day wear. Promising up to 90 hours of stamina, the Aventho Y deliver on almost every front…. except for balanced sound.</p><p>Don’t get me wrong: the Aventho Y make vocals sound strong and clear, but the rest of the soundstage, not so much. It lacks openness and sounds mostly flat. You’ll need the companion app to customize the equalizer and salvage it. Call quality isn’t great either, as the headphones make you sound scratchy.</p><p>Should you still buy these cans? Read my full Beyerdynamic Aventho Y review to find out.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-specs"><span>Beyerdynamic Aventho Y 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/beyerdynamic-AVENTHO-Wireless-Bluetooth-Headphones/dp/B0H7R8BJH3/" target="_blank" rel="nofollow">$149</a> / <a href="https://polar.uk.com/beyerdynamic-aventho-y-wireless-headphones-liquid-black" target="_blank" rel="nofollow">£129</a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Closed back</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>45mm dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Impedance</strong></p></td><td  ><p>18Ω</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP53</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 6.0 with SBC and AAC, USB-C wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></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></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>90 hours (ANC off) | 60 hours (ANC on)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>11.14 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Blue Fusion, Liquid Black, Peach Blush, Soft Jade</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-price-availability"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YgM9wVLazqK9nT93r4Mzgc" name="Bayerdynamic_Aventho_Y_Headphones_016" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/YgM9wVLazqK9nT93r4Mzgc.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Announced at IFA in September 2026, the Beyerdynamic Aventho Y are the latest over-ears to join the brand’s extensive lineup of consumer headphones. Priced at <a href="https://www.amazon.com/beyerdynamic-AVENTHO-Wireless-Bluetooth-Headphones/dp/B0H7R8BJH3/" target="_blank" rel="nofollow">$149 at Amazon U.S.</a> / <a href="https://polar.uk.com/beyerdynamic-aventho-y-wireless-headphones-liquid-black" target="_blank" rel="nofollow">£129 at Polar U.K.</a>, the Aventho Y are Beyerdynamic’s cheapest lifestyle headphones, at the time of writing. The cans come in Blue Fusion, Liquid Black, Peach Blush, and Soft Jade colors.</p><p>The Aventho Y offer ANC, long battery life with user-replaceable battery packs, and are extremely comfortable. These are some of the cheapest mid-range ANC headphones we’ve tested, and they’re cheaper than the <a href="https://www.tomsguide.com/audio/headphones/sony-wh-1000xm4c-wh-ch730n-wh-ch530-announced">Sony WH-CH730N</a> ($179), the <a href="https://www.tomsguide.com/reviews/sennheiser-accentum">Sennheiser Accentum</a> ($179), the <a href="https://www.tomsguide.com/audio/headphones/jbl-live-770nc-review">JBL Live 770NC</a> ($199), and the <a href="https://www.tomsguide.com/audio/headphones/beats-solo-4-review">Beats Solo 4</a> ($199).</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-design"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9V7YAqHtxZgJeYfXvCyo4d" name="Bayerdynamic_Aventho_Y_Headphones_015" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/9V7YAqHtxZgJeYfXvCyo4d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Beyerdynamic Aventho Y make a strong first impression with their striking colors. The Peach Blush model I tested is certainly a looker, with light and airy accents that make them stand out in a market crowded with black and dark colors. The stylized “Y” engraved into each (large) earcup is a nice touch, and the subtle Beyerdynamic branding on the headband doesn’t draw much attention either.</p><p>The Aventho Y weigh just 11.14 ounces and even though they’re slightly heavier than my trusty <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review">Marshall Monitor III</a> (8.8 ounces, $349), the difference isn’t that noticeable. They’re also heavier than their siblings, the <a href="https://www.tomsguide.com/audio/headphones/beyerdynamic-aventho-100-review">Aventho 100</a> (7.76 ounces, $229), but the added weight is a decent trade-off if you want a user-replaceable battery and a more comfortable fit.</p><p>For easy transport, the Aventho Y are foldable so you can save some space in your day bag. A drawstring cloth bag is included in the box, too.</p><h2 id="weatherproofing-2">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="A7PV2SXGV73zp9kUq4qA3d" name="Bayerdynamic_Aventho_Y_Headphones_014" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/A7PV2SXGV73zp9kUq4qA3d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 normal for over-ear headphones to not have official IP waterproof ratings, but the Aventho Y do. These cans are IP53-rated, just like the Aventho 100, so they’re protected against dust and light water splashes. You should be just fine wearing them to the gym. In contrast, the JBL Live 770NC and the Sony WH-CH730N aren’t waterproof at all — JBL and Sony don’t specify that they are, at least.</p><h2 id="comfort">Comfort</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="T4NMDWxrsvXDx6rAa9g64d" name="Bayerdynamic_Aventho_Y_Headphones_011" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/T4NMDWxrsvXDx6rAa9g64d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Aventho Y are extremely comfortable headphones, and that’s thanks to their large, spacious earcups and cushioned headband. The ear cushions feature soft foam interiors, and they’re covered in soft leatherette. These ear cushions are magnetic so they can be twisted off and replaced if they get damaged.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5v4CkXFiEch9ZXiGUMG44d" name="Bayerdynamic_Aventho_Y_Headphones_010" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/5v4CkXFiEch9ZXiGUMG44d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Similarly, the headband’s metal frame uses the same leatherette cushioning for added comfort, and it relieves the pressure you’d otherwise feel on the top of your head. Having worn the Aventho Y for a six-hour stint, I can confirm that they’re suitable for all-day wear, as my ears didn’t overheat or feel sweaty.</p><h2 id="controls-2">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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nLeBCaiHpDhDsVkU6x97nb" name="Bayerdynamic_Aventho_Y_Headphones_006" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/nLeBCaiHpDhDsVkU6x97nb.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Sporting a straightforward control scheme, everything, from volume to ANC, is adjusted via physical buttons and toggles on the Aventho Y’s earcups. You can increase or decrease the volume via the buttons on the right earcup. Located between these is a custom button which can be customized via the app to change tracks, enable voice assistant, and more. Controls for ANC and Bluetooth are on the left earcup. They’re all super straightforward to get a hang of.</p><h2 id="connectivity-2">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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pWp8DbjTzQzLLhixrnA3Gc" name="Bayerdynamic_Aventho_Y_Headphones_002" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/pWp8DbjTzQzLLhixrnA3Gc.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Aventho Y utilize Bluetooth 6.0 with support for SBC and AAC codecs. However, there’s no support for LC3, LDAC or aptX Lossless, and you’ll need to spend more on the Sennheiser Accentum if you want aptX. The lack of advanced codecs isn’t uncommon in this price range, though, as neither the JBL Live 770NC nor the Beats Solo 4 support them.</p><p>For those who want more versatile wireless connectivity, Beyerdynamic is planning on selling an optional 2.4GHz dongle (which can be handily stored inside one of the earcups) along with a detachable boom mic come October. Beyerdynamic is yet to officially announce sample rates for the dongle, but I’ve reached out to a representative to find out more. You can also use a USB-C cable for wired playback.</p><p>They also feature multipoint connectivity, enabling you to connect up to two devices simultaneously — I did so with my <a href="https://www.tomsguide.com/reviews/macbook-air-2022-m2">MacBook Air M2</a> and <a href="https://www.tomsguide.com/phones/google-pixel-phones/google-pixel-10-pro-xl-review">Google Pixel 10 Pro XL</a>. The Aventho Y say “Multipoint” out loud to you when two devices are connected at once. You also get Google Fast Pair and as soon as I put the headphones into pairing mode, my Pixel recognized and connected to them immediately.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-sound-quality"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vLyobMN2AG3DgE7EuznF6c" name="Bayerdynamic_Aventho_Y_Headphones_003" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/vLyobMN2AG3DgE7EuznF6c.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Beyerdynamic Aventho Y ulitize a 45mm driver to cover a frequency response range of 20Hz to 20KHz (so the entirety of the average human hearing spectrum). To test the headphones, I listened to my array of testing songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the<a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"> best music streaming service</a> for audiophiles — and I was left with mixed feelings.</p><p>Vocals are the Aventho Y’s strong suit. In ‘<a href="https://open.qobuz.com/track/313214062" target="_blank" rel="nofollow">Cheers to Me</a>’ by Self Esteem, Rebecca Lucy Taylor’s vocals felt raw and intimate. Each word sounded textured, every consonant felt sharp, and plosives never popped or bled into one another. The backing vocals panned wide to the far left and right, creating an immersive listening experience. As all the voices came together in the bridge, they sounded harmonious and powerful.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TopxQp9fQ7fdvzR38diX4d" name="Bayerdynamic_Aventho_Y_Headphones_013" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/TopxQp9fQ7fdvzR38diX4d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 unfortunately, the Aventho Y struggle with almost every other aspect of the soundstage. Without any EQ adjustments and straight out of the box, the Aventho Y sound boxy, hollow and lack openness. The dark pulsating bassline that drives ‘<a href="https://open.qobuz.com/album/xl54kemeg6wws" target="_blank" rel="nofollow">2515</a>’ by Wasia Project sounded like someone had turned the bass off. The song sounded closed-in and hollow, and it felt like it had lost its three-dimensionality. While the vocals still sounded defined, the rest of the track didn’t.</p><p>I then listened to ‘<a href="https://open.qobuz.com/track/80418830" target="_blank" rel="nofollow">One Inch Punch</a>’ by Yin Yin to see if the treble sounded just as flat as the bass. While the electric guitar solos shimmered, they didn’t possess the same clarity and vibrancy you get when listening to the song through a more capable set of cans. It also felt like all the instruments were pushed into the background. The bassline sounded dull as did the drums. Heavy tracks like ‘<a href="https://open.qobuz.com/album/qebcwambt9mpa" target="_blank" rel="nofollow">Put Me in a Padded Room</a>’ by Icon For Hire sounded muddled and like the headphones were easily overwhelmed.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ocA8nhQ3CgpL4YpEirNq3d" name="Bayerdynamic_Aventho_Y_Headphones_008" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/ocA8nhQ3CgpL4YpEirNq3d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Luckily, though, I was able to salvage a lot of the sound quality by adjusting the Aventho Y’s equalizer through the companion app (more on that shortly). I fixed a lot of the issues by simply playing around with the 10-band EQ and finding my ideal sound. It’s great to have such extensive customization at your fingertips, but I wish the Aventho Y sounded balanced straight out of the box — like the Sennheiser Accentum or the JBL Live 770NC.</p><p><em>Listen along to my review with the Beyerdynamic Aventho Y testing playlist:</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/69742399?zone=GB-en"></iframe><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-app"><span>Beyerdynamic Aventho Y review: App</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1510px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="gRcWZ9eDJDPYmFNCvE2Nk6" name="Aventho_Y_app" alt="Beyerdynamic app screenshots" src="https://cdn.mos.cms.futurecdn.net/gRcWZ9eDJDPYmFNCvE2Nk6.jpg" mos="" align="middle" fullscreen="1" width="1510" height="850" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/gRcWZ9eDJDPYmFNCvE2Nk6.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Beyerdynamic)</span></figcaption></figure><p>The Aventho Y are accompanied by the Beyerdynamic app, available for free on <a href="https://apps.apple.com/us/app/beyerdynamic/id6477286323" target="_blank" rel="nofollow">iOS</a> and <a href="https://play.google.com/store/apps/details?id=com.beyerdynamic.app&hl=us" target="_blank" rel="nofollow">Android</a>. It enables you to customize the controls, adjust the level of ANC, enable Low Latency mode to minimize audio delays, choose an EQ preset, and customize the 10-band eqaulizer to your liking.</p><p>Beyerdynamic’s app is easy to use and it sports a clean interface without any intrusive pop-ups. That’s good because given that you need the app to fix the Aventho Y’s sound, I’d say it’s a necessity and not an optional download.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-anc"><span>Beyerdynamic Aventho Y review: 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5c7V7sbRz2qHnuFUfjmttb" name="Bayerdynamic_Aventho_Y_Headphones_007" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/5c7V7sbRz2qHnuFUfjmttb.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Beyerdynamic Aventho Y feature effective active noise cancelation that works well indoors and outdoors. Indoors, I wasn’t able to hear the kettle boiling, the clicking-clacking of <a href="https://www.tomsguide.com/best-picks/best-mechanical-keyboards">mechanical keyboards</a>, or other people talking. My partner had to wave several times to get my attention when I was testing the Aventho Y.</p><p>On a busy bus back from work, the Aventho Y managed to drown out the sound of people talking loudly. While I could hear the sound of the bus rattling, it wasn’t intrusive or too much, and the Aventho Y did a good job of ensuring I felt immersed in my tunes.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-call-quality"><span>Beyerdynamic Aventho Y review: 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2rSh22ao74zeJmhAS7S63d" name="Bayerdynamic_Aventho_Y_Headphones_012" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/2rSh22ao74zeJmhAS7S63d.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Unfortunately, the Beyerdynamic Aventho Y’s call quality is quite disappointing. On a Google Meet team call, my colleagues winced when I started speaking — and they’ve heard me talk before so I know it isn’t <em>me. </em>I asked them how I sounded, and they noted that my voice was scratchy, distant, and “not great at all.”</p><p>To rule out the possibility that this was down to an unstable internet connection, I called my partner who said that my voice sounded muffled and she couldn’t understand my words very clearly. I’d recommend the Sennheiser Accentum if you want better call quality.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-battery-life"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bCuh4rrDJR3ggxnwVrNESc" name="Bayerdynamic_Aventho_Y_Headphones_004" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/bCuh4rrDJR3ggxnwVrNESc.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>An area the Beyerdynamic Aventho Y perform formidably is battery life. Beyerdynamic says that on a single charge, the Aventho Y can last up to 90 hours with ANC off, or 60 hours with ANC on. This is excellent, and the headphones outclass the Sennheiser Accentum (50 hours), the Sony WH-CH730N (50 hours) and the JBL Tune 770NC (44 hours) when ANC is enabled.</p><p>With ANC enabled and music playing at 50% volume, I left the Aventho Y running over a few nights. After approximately 50 hours, I had just 5% battery remaining. Beyerdynamic’s battery claims are, then, fairly reliable.</p><p>The Aventho Y feature a user-replaceable battery, which increases the longevity of the headphones. The manual specifies how to remove the battery carefully and safely, should you need to do so.</p><h2 class="article-body__section" id="section-beyerdynamic-aventho-y-review-verdict"><span>Beyerdynamic Aventho Y 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="MeB7TVLnzKUXmG7wL5FW5c" name="Bayerdynamic_Aventho_Y_Headphones_009" alt="Beyerdynamic Aventho Y over-ear headphones" src="https://cdn.mos.cms.futurecdn.net/MeB7TVLnzKUXmG7wL5FW5c.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" endorsement="" class="inline"></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 Beyerdynamic Aventho Y check almost every box. They boast up to 90 hours of battery life, effective ANC indoors and outdoors, a comfortable fit, a colorful design, and they come with a user-friendly companion app. Vocals are clear, strong and nuanced, yes, but everything else sounds flat — and that’s no bueno for a pair of headphones.</p><p>While the sound quality is salvageable thanks to the custom 10-band equalizer, it’s a shame that the Aventho Y don’t sound great straight out of the box. The bass sounds muddy and the treble struggles to shine. Call quality isn’t great either as several people noted that I sounded scratchy on their end.</p><p>Though the Aventho Y are fun-looking, comfortable and last a long time, it’s worth spending a little more on the likes of the JBL Live 770NC if you want solid sound as soon as you unbox the cans. At the end of the day, the Aventho Y deliver on some fronts but fall flat where it matters most.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Yoto Player 4th Gen unveiled with 'Green Button' playlist feature — and there's a new Yoto mini player, too ]]></title>
                                                                                                <dc:content><![CDATA[ <p>If you're a parent looking for an alternative screen-free device for your kids, then there's a good chance you'll have heard of Yoto Player. </p><p>The card-based audio player with no microphone and no camera has been <a href="https://www.tomsguide.com/audio/i-tested-the-toniebox-and-yoto-player-for-2-weeks-and-they-helped-my-kids-cut-down-their-tv-time">a breakout hit for both kids and preteens</a>. There's no screen, just a mosaic-based display that calls up cutesy images related to whatever is playing. And thanks to the huge amount of content already available through Yoto's NFC cards, everything from Roald Dahl to <a href="https://www.tomsguide.com/entertainment/netflix/kpop-demon-hunters-is-officially-netflixs-most-watched-movie-of-all-time-and-a-sequel-could-be-on-the-way">KPop Demon Hunters</a> and Bluey is available on the platform.</p><p>Today, Yoto announced its 4th Generation Yoto Player and Yoto Mini, three years on from the last iteration. </p><p>The headline feature for both players is a new physical "Green Button" that enables cardless playback of up to 20 items that parents can program via the Yoto app. There's also been a boost of internal storage up to 64GB, upgraded Wi-Fi 6 and Bluetooth 6.1, and an IP67 durability rating for the portable Mini.</p><p>"This isn't just a new product, it’s a new platform — we’ve upgraded everything under the hood," said Ben Drury, CEO and Co-founder of Yoto. "We've spent three years building this foundation for the next chapter of Yoto — faster, more reliable, and with kids in control, just as they've always been."</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Xk8v3e"></div>                            </div>                            <script src="https://kwizly.com/embed/Xk8v3e.js" async></script><h2 id="the-green-button">The Green Button</h2><p>The standout feature across both 4th Gen models is the new Green Button. This acts as a sort of 'favorites' option for kids to collect up to 20 stories, songs, podcasts, soundscapes or other pieces of content and play them without needing to put in a card.</p><p>Parents manage these shortcuts directly through the Yoto app, allowing separate playlists for morning routines and bedtime wind-downs. It also grants immediate access to digital-only app content, such as toothbrushing timers and sleep soundscapes.</p><h2 id="additional-upgrades-across-both-players">Additional upgrades across both players</h2><p><strong>Better Connectivity</strong> Under the hood, both 4th Gen devices are equipped with Wi-Fi 6 and Bluetooth 6.1 for faster audio downloads and reliable wireless headphone or app pairing. They also have newer processors for faster performance.</p><p><strong>Expanded Storage:</strong> Internal memory has doubled to 64GB, capable of holding over 1,000 hours of offline audio for travel.</p><p><strong>Extended Warranty:</strong> All 4th Gen devices now ship with a standard two-year warranty.</p><h2 id="yoto-player-vs-yoto-mini-specific-hardware-upgrades">Yoto Player vs Yoto Mini: Specific hardware 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="ieoXq37ikb577Z7nyF7KCB" name="Yoto Player Mini 4th gen" alt="Yoto Player 4th Generation" src="https://cdn.mos.cms.futurecdn.net/ieoXq37ikb577Z7nyF7KCB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Yoto / Tom's Guide)</span></figcaption></figure><p>Alongside the core upgrades, each 4th Gen player is also receiving some specific hardware improvements tailored to what they're used for.</p><p>The larger stay-at-home Yoto Player 4th Gen has been given an upgraded full-panel night light supporting seven customizable colors, complete with dynamic day/night visual clock cues and Ok-to-Wake indicators. </p><p>The player's audio quality is enhanced via an acoustically engineered stereo speaker array, and charging is handled via USB-C or wireless charging bases. Yoto also confirmed a user-replaceable battery replacement kit will be made available in 2027 to extend device longevity.</p><p>Meanwhile, the portable Yoto Mini 4th Gen has been given a focus on ruggedness. Yoto has confirmed a new IP67 dust and water resistance rating, rendering it fully splashproof, puddle-proof, and drop-tested. Something that many parents will no doubt appreciate. The Yoto Mini 4th Gen maintains support for both 3.5mm wired headphones and Bluetooth pairing, alongside USB-C fast charging.</p><h2 id="yoto-player-4th-gen-availability-and-pricing">Yoto Player 4th Gen availability and pricing</h2><p>Both devices are available for pre-order starting today via <a href="https://us.yotoplay.com/" target="_blank" rel="nofollow">Yoto's direct store</a>, with the Mini also listed on Amazon. The players will also be available through other retailers soon.</p><p>The Yoto Player 4th Generation is priced at £119.99 / $149.99 while the Mini is priced at $109.99 / £89.99.</p><p>The Yoto Mini 4th Gen will begin shipping September 30, 2026, while the full-sized Yoto Player 4th Gen will start shipping on October 15.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><strong></strong><a href="https://www.tomsguide.com/audio/i-tested-the-toniebox-and-yoto-player-for-2-weeks-and-they-helped-my-kids-cut-down-their-tv-time"><strong>I tested the Toniebox and Yoto Player for 2 weeks, and they helped my kids cut down their TV time</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/audio/i-tried-a-music-hammock-that-vibrates-to-the-beat-of-any-song-and-i-wanted-to-take-it-home-with-me"><strong>I tried a music hammock that vibrates to the beat of any song</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/audio/headphones/sony-wh-1000xm4c-wh-ch730n-wh-ch530-announced"><strong>Sony just refreshed three of its most popular headphones with replaceable batteries and punchier sound</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/yoto-player-4th-gen-unveiled-with-green-button-playlist-feature-and-theres-a-new-yoto-mini-player-too</link>
                                                                            <description>
                            <![CDATA[ The screen-free audio player for kids has been updated with double the storage, Wi-Fi 6 and a Green Button cardless playlist system. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">fzBsk2r2EqeapAUtR4R4fA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/xNzJFscLjP2p4gX3u4KwCB-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 10 Sep 2026 09:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                <author><![CDATA[ jeff.parsons@futurenet.com (Jeff Parsons) ]]></author>                    <dc:creator><![CDATA[ Jeff Parsons ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/7z3UTGGrmSokMKxTWHmhjX.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Jeff is U.K. Editor-in-Chief for Tom’s Guide looking after the day-to-day output of the site’s British contingent. &lt;/p&gt;&lt;p&gt;Rising early and heading straight for the coffee machine, Jeff loves nothing more than dialling into the zeitgeist of the day’s tech news. A journalist for over a decade, he&#039;s travelled around the world testing and reviewing any gadget he can get his hands on.&lt;/p&gt;&lt;p&gt;Before joining the team at Tom’s Guide, Jeff covered technology and science for two of the U.K.’s biggest national news sites: Metro.co.uk and the Daily Mirror. Memorable moments include getting lost in Vienna in an electric Audi, touring Lockheed Martin’s mile-long jet factory in Fort Worth and filming a Netflix documentary about Elon Musk in West London.&lt;/p&gt;&lt;p&gt;When not plugged into the current news agenda, editing or commissioning a series of articles or debating the merits of Apple vs Android, Jeff can usually be found out for a run trying to shave precious seconds off his PB. Or lifting weights in a vain attempt to offset the ageing process.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/xNzJFscLjP2p4gX3u4KwCB-1280-80.jpg">
                                                            <media:credit><![CDATA[Yoto / Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Yoto Player 4th Generation]]></media:description>                                                            <media:text><![CDATA[Yoto Player 4th Generation]]></media:text>
                                <media:title type="plain"><![CDATA[Yoto Player 4th Generation]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/xNzJFscLjP2p4gX3u4KwCB-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you're a parent looking for an alternative screen-free device for your kids, then there's a good chance you'll have heard of Yoto Player. </p><p>The card-based audio player with no microphone and no camera has been <a href="https://www.tomsguide.com/audio/i-tested-the-toniebox-and-yoto-player-for-2-weeks-and-they-helped-my-kids-cut-down-their-tv-time">a breakout hit for both kids and preteens</a>. There's no screen, just a mosaic-based display that calls up cutesy images related to whatever is playing. And thanks to the huge amount of content already available through Yoto's NFC cards, everything from Roald Dahl to <a href="https://www.tomsguide.com/entertainment/netflix/kpop-demon-hunters-is-officially-netflixs-most-watched-movie-of-all-time-and-a-sequel-could-be-on-the-way">KPop Demon Hunters</a> and Bluey is available on the platform.</p><p>Today, Yoto announced its 4th Generation Yoto Player and Yoto Mini, three years on from the last iteration. </p><p>The headline feature for both players is a new physical "Green Button" that enables cardless playback of up to 20 items that parents can program via the Yoto app. There's also been a boost of internal storage up to 64GB, upgraded Wi-Fi 6 and Bluetooth 6.1, and an IP67 durability rating for the portable Mini.</p><p>"This isn't just a new product, it’s a new platform — we’ve upgraded everything under the hood," said Ben Drury, CEO and Co-founder of Yoto. "We've spent three years building this foundation for the next chapter of Yoto — faster, more reliable, and with kids in control, just as they've always been."</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Xk8v3e"></div>                            </div>                            <script src="https://kwizly.com/embed/Xk8v3e.js" async></script><h2 id="the-green-button">The Green Button</h2><p>The standout feature across both 4th Gen models is the new Green Button. This acts as a sort of 'favorites' option for kids to collect up to 20 stories, songs, podcasts, soundscapes or other pieces of content and play them without needing to put in a card.</p><p>Parents manage these shortcuts directly through the Yoto app, allowing separate playlists for morning routines and bedtime wind-downs. It also grants immediate access to digital-only app content, such as toothbrushing timers and sleep soundscapes.</p><h2 id="additional-upgrades-across-both-players">Additional upgrades across both players</h2><p><strong>Better Connectivity</strong> Under the hood, both 4th Gen devices are equipped with Wi-Fi 6 and Bluetooth 6.1 for faster audio downloads and reliable wireless headphone or app pairing. They also have newer processors for faster performance.</p><p><strong>Expanded Storage:</strong> Internal memory has doubled to 64GB, capable of holding over 1,000 hours of offline audio for travel.</p><p><strong>Extended Warranty:</strong> All 4th Gen devices now ship with a standard two-year warranty.</p><h2 id="yoto-player-vs-yoto-mini-specific-hardware-upgrades">Yoto Player vs Yoto Mini: Specific hardware 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="ieoXq37ikb577Z7nyF7KCB" name="Yoto Player Mini 4th gen" alt="Yoto Player 4th Generation" src="https://cdn.mos.cms.futurecdn.net/ieoXq37ikb577Z7nyF7KCB.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Yoto / Tom's Guide)</span></figcaption></figure><p>Alongside the core upgrades, each 4th Gen player is also receiving some specific hardware improvements tailored to what they're used for.</p><p>The larger stay-at-home Yoto Player 4th Gen has been given an upgraded full-panel night light supporting seven customizable colors, complete with dynamic day/night visual clock cues and Ok-to-Wake indicators. </p><p>The player's audio quality is enhanced via an acoustically engineered stereo speaker array, and charging is handled via USB-C or wireless charging bases. Yoto also confirmed a user-replaceable battery replacement kit will be made available in 2027 to extend device longevity.</p><p>Meanwhile, the portable Yoto Mini 4th Gen has been given a focus on ruggedness. Yoto has confirmed a new IP67 dust and water resistance rating, rendering it fully splashproof, puddle-proof, and drop-tested. Something that many parents will no doubt appreciate. The Yoto Mini 4th Gen maintains support for both 3.5mm wired headphones and Bluetooth pairing, alongside USB-C fast charging.</p><h2 id="yoto-player-4th-gen-availability-and-pricing">Yoto Player 4th Gen availability and pricing</h2><p>Both devices are available for pre-order starting today via <a href="https://us.yotoplay.com/" target="_blank" rel="nofollow">Yoto's direct store</a>, with the Mini also listed on Amazon. The players will also be available through other retailers soon.</p><p>The Yoto Player 4th Generation is priced at £119.99 / $149.99 while the Mini is priced at $109.99 / £89.99.</p><p>The Yoto Mini 4th Gen will begin shipping September 30, 2026, while the full-sized Yoto Player 4th Gen will start shipping on October 15.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><strong></strong><a href="https://www.tomsguide.com/audio/i-tested-the-toniebox-and-yoto-player-for-2-weeks-and-they-helped-my-kids-cut-down-their-tv-time"><strong>I tested the Toniebox and Yoto Player for 2 weeks, and they helped my kids cut down their TV time</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/audio/i-tried-a-music-hammock-that-vibrates-to-the-beat-of-any-song-and-i-wanted-to-take-it-home-with-me"><strong>I tried a music hammock that vibrates to the beat of any song</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/audio/headphones/sony-wh-1000xm4c-wh-ch730n-wh-ch530-announced"><strong>Sony just refreshed three of its most popular headphones with replaceable batteries and punchier sound</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Apple AirPods 5 preorders — here's how to get yours today ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The AirPods family just got bigger. Apple officially unveiled its <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-5-officially-announced-here-are-the-upgrades">AirPods 5</a> at its "Surprise and Shine" keynote. And whereas most of the devices announced at the keynote included higher prices, the AirPods 5 are actually cheaper than their predecessors. </p><p>AirPods 5 preorders are available now <a href="https://www.amazon.com/dp/B0HJB69GJL" target="_blank" rel="nofollow">for $129 with ANC at Amazon</a> or <a href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" rel="nofollow">$149 with a Wireless Charging Case at Amazon</a>. That's cheaper than the AirPods 4 which cost $129 without ANC and $179 with ANC.  </p><p>The AirPods 5 feature the same H3 Wireless Chip found in previous models, but usher in live translation, full support for Siri AI, and 50% more noise reduction than the previous generation. They'll be available in stores on September 18. </p><p>Currently, there are no AirPods 5 deals available. However, it's only a matter of time before a retailer like Amazon or Best Buy knocks $10 off. (For more Apple deals, make sure to check out our <a href="https://www.tomsguide.com/coupons/apple.com">Apple Store coupons</a> coverage).  </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><strong>AirPods 5 w/ ANC: </strong><a href="https://www.amazon.com/dp/B0HJB69GJL" target="_blank" rel="nofollow"><strong>for $129 @ Amazon</strong></a><strong></strong></li><li><strong>AirPods 5 w/ Wireless Charging Case: </strong><a href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" data-rewrite="keep"><strong>for $149 @ Amazon</strong></a></li><li><strong>AirPods 5 w/ ANC: </strong><a href="https://www.walmart.com/ip/Apple-AirPods-5/20951672018" target="_blank" rel="nofollow"><strong>for $129 @ Walmart</strong></a><strong></strong></li><li><strong>AirPods 5 w/ Wireless Charging Case: </strong><a href="https://www.walmart.com/ip/Apple-AirPods-5-with-Wireless-Charging-Case/20991207083" target="_blank" rel="nofollow"><strong>for $149 @ Walmart</strong></a></li><li><strong>AirPods 5 w/ ANC: </strong><a href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-white/JJGCQP68Y2/sku/6376553" target="_blank" rel="nofollow"><strong>for $129 @ Best Buy</strong></a></li><li><strong>AirPods 5 w/ Wireless Charging Case: </strong><a href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-with-wireless-charging-case-white/JJGCQP634G/sku/6376564" target="_blank" rel="nofollow"><strong>for $149 @ Best Buy</strong></a></li><li><strong>AirPods 5 w/ ANC: </strong><a href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow"><strong>for $129 @ Apple</strong></a></li><li><strong>AirPods 5 w/ Wireless Charging Case: </strong><a href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow"><strong>for $149 @ Apple</strong></a></li></ul><h3 class="article-body__section" id="section-apple-airpods-5-preorders"><span>Apple AirPods 5 preorders</span></h3><div class="product"><a data-dimension112="b1a198b6-ac8b-11f1-af35-79a83f1df88a" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129" href="https://www.amazon.com/dp/B0HJB69GJL" 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:94.40%;"><img id="mJGKbeqw2FwvrHngHh7KUc" name="Apple AirPods 5 deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/mJGKbeqw2FwvrHngHh7KUc.jpg" mos="" align="middle" fullscreen="" width="1500" height="1416" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0HJB69GJL" target="_blank" rel="nofollow" data-dimension112="b1a198b6-ac8b-11f1-af35-79a83f1df88a" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">AirPods 5 w/ ANC: $129</a></strong><br>Amazon is now offering preorders of the Apple AirPods 5 with ANC. They feature improved noise cancellation, better sound/call quality, Live Translation, and hands-free access to Siri AI. They offer 4 hours of battery life on a single charge. They will be released on September 18. <a class="view-deal button" href="https://www.amazon.com/dp/B0HJB69GJL" target="_blank" rel="nofollow" data-dimension112="b1a198b6-ac8b-11f1-af35-79a83f1df88a" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="e6be4880-ad23-11f1-aad4-6510a933cb60" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149" href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1116px;"><p class="vanilla-image-block" style="padding-top:134.41%;"><img id="UzZQpdMReHRGTHi5gnLv89" name="AirPods 5 w: Wireless Charging Case deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/UzZQpdMReHRGTHi5gnLv89.jpg" mos="" align="middle" fullscreen="" width="1116" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" rel="nofollow" data-dimension112="e6be4880-ad23-11f1-aad4-6510a933cb60" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">AirPods 5 w/ Wireless Charging Case: $149</a></strong><br>Amazon is now offering preorders of the Apple AirPods 5 with Wireless Charging Case. They feature improved noise cancellation, better sound/call quality, Live Translation, and hands-free access to Siri AI. The upgraded model also offers 5 hours of battery life on a single charge and a touch-sensitive stem for controlling music. They will be released on September 18. <a class="view-deal button" href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" rel="nofollow" data-dimension112="e6be4880-ad23-11f1-aad4-6510a933cb60" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b1a19a1e-ac8b-11f1-ba83-2958700e75cf" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129" href="https://www.walmart.com/ip/Apple-AirPods-5/20951672018" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:960px;"><p class="vanilla-image-block" style="padding-top:93.65%;"><img id="Bi2iDjTxUHTUQK9FHZ4W8D" name="Walmart logo.jpeg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Bi2iDjTxUHTUQK9FHZ4W8D.jpeg" mos="" align="middle" fullscreen="" width="960" height="899" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.walmart.com/ip/Apple-AirPods-5/20951672018" target="_blank" rel="nofollow" data-dimension112="b1a19a1e-ac8b-11f1-ba83-2958700e75cf" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">AirPods 5 w/ ANC: $129</a></strong><br>The new Apple AirPods 5 are now available for preorder at Walmart. Selling for $129 the improved AirPods will ship on September 18. <a class="view-deal button" href="https://www.walmart.com/ip/Apple-AirPods-5/20951672018" target="_blank" rel="nofollow" data-dimension112="b1a19a1e-ac8b-11f1-ba83-2958700e75cf" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="fef55146-ae18-11f1-b3b6-3715ddf4cf4a" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149" href="https://www.walmart.com/ip/Apple-AirPods-5-with-Wireless-Charging-Case/20991207083" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:960px;"><p class="vanilla-image-block" style="padding-top:93.65%;"><img id="Bi2iDjTxUHTUQK9FHZ4W8D" name="Walmart logo.jpeg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Bi2iDjTxUHTUQK9FHZ4W8D.jpeg" mos="" align="middle" fullscreen="" width="960" height="899" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.walmart.com/ip/Apple-AirPods-5-with-Wireless-Charging-Case/20991207083" target="_blank" rel="nofollow" data-dimension112="fef55146-ae18-11f1-b3b6-3715ddf4cf4a" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">AirPods 5 w/ Wireless Charging Case: $149</a></strong><br>The new Apple AirPods 5 are now available for preorder at Walmart. In addition to a wireless charging case, this upgraded model also offers 5 hours of battery life on a single charge and a touch-sensitive stem for controlling music. <a class="view-deal button" href="https://www.walmart.com/ip/Apple-AirPods-5-with-Wireless-Charging-Case/20991207083" target="_blank" rel="nofollow" data-dimension112="fef55146-ae18-11f1-b3b6-3715ddf4cf4a" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b1a1996a-ac8b-11f1-b7da-4da6f45689a1" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129" href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-white/JJGCQP68Y2/sku/6376553" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1331px;"><p class="vanilla-image-block" style="padding-top:68.52%;"><img id="yjsgCbZzcWDLMuhHtLHa7K" name="Best Buy deal block 2022.jpeg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yjsgCbZzcWDLMuhHtLHa7K.jpeg" mos="" align="middle" fullscreen="" width="1331" height="912" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-white/JJGCQP68Y2/sku/6376553" target="_blank" rel="nofollow" data-dimension112="b1a1996a-ac8b-11f1-b7da-4da6f45689a1" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">AirPods 5 w/ ANC: $129</a></strong><br>The Apple AirPods 5 are now available at Best Buy. The base model includes active noise cancellation, Live Translation, and hands-free access to Siri AI. They offer 4 hours of battery life on a single charge. <a class="view-deal button" href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-white/JJGCQP68Y2/sku/6376553" target="_blank" rel="nofollow" data-dimension112="b1a1996a-ac8b-11f1-b7da-4da6f45689a1" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="fef551dc-ae18-11f1-9469-bf0fbb03a301" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149" href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-with-wireless-charging-case-white/JJGCQP634G/sku/6376564" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1331px;"><p class="vanilla-image-block" style="padding-top:68.52%;"><img id="yjsgCbZzcWDLMuhHtLHa7K" name="Best Buy deal block 2022.jpeg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yjsgCbZzcWDLMuhHtLHa7K.jpeg" mos="" align="middle" fullscreen="" width="1331" height="912" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-with-wireless-charging-case-white/JJGCQP634G/sku/6376564" target="_blank" rel="nofollow" data-dimension112="fef551dc-ae18-11f1-9469-bf0fbb03a301" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">AirPods 5 w/ Wireless Charging Case: $149</a></strong><br>The Apple AirPods 5 with Wireless Charging Case have all the same features as the base model, but also a wireless charging case, 5 hours of battery life on a single charge, and a touch-sensitive stem for controlling music. <a class="view-deal button" href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-with-wireless-charging-case-white/JJGCQP634G/sku/6376564" target="_blank" rel="nofollow" data-dimension112="fef551dc-ae18-11f1-9469-bf0fbb03a301" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b1a19820-ac8b-11f1-b2eb-ed8a3f2d7638" data-action="Deal Block" data-label="AirPods 5" data-dimension48="AirPods 5" data-dimension25="$129" href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:600px;"><p class="vanilla-image-block" style="padding-top:66.67%;"><img id="4Y3oAjs2dPF6xp4nhVsVcc" name="Applelogo.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4Y3oAjs2dPF6xp4nhVsVcc.jpg" mos="" align="middle" fullscreen="" width="600" height="400" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods 5 are available for pre-order at Apple today. They offer live translation, full support for Siri AI, and 50% more noise reduction than the previous generation. They're also slightly cheaper than their predecessor with the ANC model selling for $129 and the AirPods 5 with Wireless Charging Case for $149. You can get your AirPods 5 engraved and personalized for free when you preorder them. Plus, Apple is also giving you three months of Apple Music with your purchase. <a class="view-deal button" href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow" data-dimension112="b1a19820-ac8b-11f1-b2eb-ed8a3f2d7638" data-action="Deal Block" data-label="AirPods 5" data-dimension48="AirPods 5" data-dimension25="$129">View Deal</a></p></div><h2 id="apple-airpods-5-what-39-s-new">Apple AirPods 5 — what's new?</h2><p>The AirPods 5 have an open ear fit with next-gen adaptive EQ. You also get better Siri features, real-time Live Translation, and a durable design that can withstand rain. In terms of battery life, you'll get up to 4 hours with ANC or up to 20 hours with charging case. The upgraded AirPods 5 model has a touch sensitive stem that offers 5 hours of battery with ANC and comes with a wireless charging case, all priced at $149. </p><p>Apple claims they remove up to 50% more noise than the AirPods 4 with ANC thanks to the new multiport acoustic architecture and improved computational audio algorithms. There's also a new Transparency mode that more naturally represents voices and nearby sounds, helping users stay connected to their surroundings.</p><h3 class="article-body__section" id="section-shop-more-deals"><span>Shop more deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?brands=Apple&rows=6&view_mode=savings_squad&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function getTimeAgo(dateString) {        if (!dateString) return '';        const date = new Date(dateString);        const now = new Date();        const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);        if (diffInSeconds < 60) return 'Just now';        const diffInMinutes = Math.floor(diffInSeconds / 60);        if (diffInMinutes < 60) return `${diffInMinutes} min${diffInMinutes > 1 ? 's' : ''} ago`;        const diffInHours = Math.floor(diffInMinutes / 60);        if (diffInHours < 24) return `${diffInHours} hr${diffInHours > 1 ? 's' : ''} ago`;        const diffInDays = Math.floor(diffInHours / 24);        if (diffInDays < 30) return `${diffInDays} day${diffInDays > 1 ? 's' : ''} ago`;        const diffInMonths = Math.floor(diffInDays / 30);        if (diffInMonths < 12) return `${diffInMonths} mo${diffInMonths > 1 ? 's' : ''} ago`;        const diffInYears = Math.floor(diffInDays / 365);        return `${diffInYears} yr${diffInYears > 1 ? 's' : ''} ago`;      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;      position: relative;      z-index: 20;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      margin: 0 auto;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters-container {      position: relative;      width: 100%;      max-width: 800px;    }    .tg-df-scroll-btn {      display: none;      position: absolute;      top: 50%;      transform: translateY(-50%);      width: 32px;      height: 32px;      background: white;      border: 1px solid var(--tg-df-border);      border-radius: 50%;      align-items: center;      justify-content: center;      cursor: pointer;      z-index: 10;      box-shadow: 0 2px 8px rgba(0,0,0,0.1);      color: var(--tg-df-text-primary);      padding: 0;    }    .tg-df-scroll-btn svg {      width: 16px;      height: 16px;    }    .tg-df-scroll-btn:hover {      background: #f4f4f4;    }    .tg-df-scroll-btn.left {      left: 0px;    }    .tg-df-scroll-btn.right {      right: 0px;    }    @container tg-df (max-width: 768px) {      .tg-df-scroll-btn {        display: flex;        top: 22px; /* vertically center within the 44px high filter buttons */      }    }    .tg-df-filters {      display: grid;      width: 100%;      grid-template-columns: repeat(4, 1fr);      gap: 12px;      margin: 0 auto;      max-width: 800px;    }                 .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;      width: 100%;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      width: 100%;      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }    @container tg-df (max-width: 768px) {      .tg-df-filters-container {      position: relative;      width: 100%;      max-width: 800px;    }    .tg-df-scroll-btn {      display: none;      position: absolute;      top: 50%;      transform: translateY(-50%);      width: 32px;      height: 32px;      background: white;      border: 1px solid var(--tg-df-border);      border-radius: 50%;      align-items: center;      justify-content: center;      cursor: pointer;      z-index: 10;      box-shadow: 0 2px 8px rgba(0,0,0,0.1);      color: var(--tg-df-text-primary);      padding: 0;    }    .tg-df-scroll-btn svg {      width: 16px;      height: 16px;    }    .tg-df-scroll-btn:hover {      background: #f4f4f4;    }    .tg-df-scroll-btn.left {      left: 0px;    }    .tg-df-scroll-btn.right {      right: 0px;    }    @container tg-df (max-width: 768px) {      .tg-df-scroll-btn {        display: flex;        top: 22px; /* vertically center within the 44px high filter buttons */      }    }    .tg-df-filters {        width: 100%;        margin: 0;        margin-bottom: -320px;        padding: 0 16px 320px 16px;        display: flex;        flex-wrap: nowrap;        gap: 8px;        overflow-x: auto;        overflow-y: hidden;        pointer-events: none;        scrollbar-width: none;        -webkit-overflow-scrolling: touch;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }      .tg-df-sort-wrapper {        pointer-events: auto;        flex: 0 0 auto;        width: 175px;        min-width: 175px;      }    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 120px;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 16px 16px 8px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }                  .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;        width: auto;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;      width: 100vw;      max-width: 1200px;      position: relative;      left: 50%;      transform: translateX(-50%);    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 14px;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    .tg-df-grid-wrapper { position: relative; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn {      display: inline-flex;      align-items: center;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.40);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #1f69ff; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #0056e0; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }    .tg-df-savings-squad-header { margin-bottom: 24px; text-align: center; display: none; }    .tg-df-banner-img-desktop { display: block; width: 100%; height: auto; margin-bottom: 32px; }    .tg-df-banner-img-mobile { display: none; width: 100%; height: auto; margin-bottom: 32px; }    @container tg-df (max-width: 600px) {      .tg-df-banner-img-desktop { display: none; }      .tg-df-banner-img-mobile { display: block; }    }    .tg-df-header-title { font-size: 28px; font-weight: 700; color: var(--tg-df-text); margin: 32px 0 12px 0; line-height: 1.3; }    .tg-df-header-subtitle { font-size: 16px; color: var(--tg-df-text-muted); margin: 0 0 32px 0; line-height: 1.5; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3Cdiv class="tg-df-savings-squad-header" id="tg-df-savings-squad-header">      \x3Cpicture>        \x3Cimg src="https://cdn.mos.cms.futurecdn.net/flexiimages/xkh2og7m3d1778189998.png" alt="Deals Banner" class="tg-df-banner-img-desktop" />        \x3Cimg src="https://cdn.mos.cms.futurecdn.net/flexiimages/gmak6rtdf41778245089.png" alt="Deals Banner Mobile" class="tg-df-banner-img-mobile" />      \x3C/picture>      \x3Cdiv class="tg-df-header-text">        \x3Ch2 class="tg-df-header-title" id="tg-df-header-title">Editor's Choice Deals\x3C/h2>        \x3Cp class="tg-df-header-subtitle" id="tg-df-header-subtitle">Discover the best discounts currently available, curated daily by the Tom's Guide Savings Squad.\x3C/p>      \x3C/div>    \x3C/div>    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Televisions" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/wcMxTsHgqu3roMbAx7RLnT-132-100.png" alt="TVs" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">TVs\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Phones" data-pr="over50">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/G3KGaRGzj24F6PUsw4bWpT-132-100.png" alt="Phones" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Phones\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Computing" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/znNvsLzx8NEgNkD9HSFSnT-132-100.png" alt="Computing" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Computing\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Gaming" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/Pgew8yaRQeZFHqHjTzvBnT-132-100.png" alt="Gaming" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Gaming\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Mattresses" data-pr="over500">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/cW7xsaLyesxkHFVSiC4kmT-132-100.png" alt="Mattresses" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Mattresses\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Audio" data-pr="over30">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/pCvBVHuhaQVjKt3VgCjbqT-132-100.png" alt="Audio" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Audio\x3C/span>            \x3C/div>                  \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E      \x3Cdiv class="tg-df-top-bar" id="tg-df-top-bar" style="position: relative; z-index: 100; margin: 0 auto 20px;">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>      \x3C/div>    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">              \x3Cdiv class="tg-df-filters-container" style="position: relative; width: 100%; max-width: 800px; margin: 0 auto;">          \x3Cbutton class="tg-df-scroll-btn left" style="display: none;" onclick="this.parentElement.querySelector('.tg-df-filters').scrollBy({left: -200, behavior: 'smooth'})">            \x3Csvg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\x3Cpath d="M15 18l-6-6 6-6"/>\x3C/svg>          \x3C/button>          \x3Cbutton class="tg-df-scroll-btn right" style="display: none;" onclick="this.parentElement.querySelector('.tg-df-filters').scrollBy({left: 200, behavior: 'smooth'})">            \x3Csvg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\x3Cpath d="M9 18l6-6-6-6"/>\x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-filters">          \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>          \x3C/div>        \x3C/div>        \x3C/div>      \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();        }        getViewMode() {          console.log("DEBUG getViewMode -> override:", this.viewModeOverride, "editorMode:", this.editorMode);          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          console.log("[DEBUG] applyLayoutMode CALLED! mode=", mode);          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          const showHeaderDetails = params.get('show_header_details') !== 'false';          const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          const topBarDiv = this.root.querySelector('#tg-df-top-bar');          const headerElement = this.root.querySelector('#tg-df-savings-squad-header');          if (headerElement) {             headerElement.style.display = (mode === 'savings_squad' && showHeaderDetails) ? 'block' : 'none';          }          if (mode === 'carousel') {             this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (topBarDiv) topBarDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.add('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (topBarDiv) topBarDiv.style.display = 'block';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!this.viewModeOverride && initialViewMode) {            this.viewModeOverride = initialViewMode;          }          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Gaming laptops';          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (this.viewModeSelect && params.has('view_mode')) {            this.viewModeSelect.value = params.get('view_mode');          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'date_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }          const headerTitleEl = this.root.querySelector('#tg-df-header-title');          const headerSubtitleEl = this.root.querySelector('#tg-df-header-subtitle');          if (params.has('widget_title') && headerTitleEl) {             headerTitleEl.textContent = params.get('widget_title');          }          if (params.has('widget_subtitle') && headerSubtitleEl) {             headerSubtitleEl.textContent = params.get('widget_subtitle');          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            if (this.getViewMode() === 'savings_squad') {              this.searchInput.value = '';            } else {              this.searchInput.value = this.currentQuery;            }          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'date_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const handleFiltersScroll = () => {            const filters = this.root.querySelector('.tg-df-filters');            const leftBtn = this.root.querySelector('.tg-df-scroll-btn.left');            const rightBtn = this.root.querySelector('.tg-df-scroll-btn.right');            if (filters && leftBtn && rightBtn) {              const { scrollLeft, scrollWidth, clientWidth } = filters;              leftBtn.style.display = scrollLeft > 0 ? 'flex' : 'none';              rightBtn.style.display = Math.ceil(scrollLeft + clientWidth) < scrollWidth - 5 ? 'flex' : 'none';            }          };          const filters = this.root.querySelector('.tg-df-filters');          if (filters) {            filters.addEventListener('scroll', handleFiltersScroll);            window.addEventListener('resize', handleFiltersScroll);            setTimeout(handleFiltersScroll, 100);                        // Also call after rendering dropdowns            const origRenderCategories = this.renderCategories;            if (origRenderCategories) {               this.renderCategories = (...args) => {                 origRenderCategories.apply(this, args);                 setTimeout(handleFiltersScroll, 50);               };            }          }                const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          const showAutocomplete = () => {             if (this.getViewMode() !== 'savings_squad' || !this.autocompleteDropdown || !this.airedaleTags) return;                          let terms = this.airedaleTags;             if (this.airedaleBrands) {                terms = terms.concat(this.airedaleBrands.map(b => b.formatted_value));             }             terms = [...new Set(terms)];                          const query = this.searchInput.value.trim();             let matches = [];             if (query.length > 0) {                 matches = terms.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase());             } else {                 matches = terms;             }                          if (matches.length > 0) {                 this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                 this.autocompleteDropdown.classList.add('active');             } else {                 this.autocompleteDropdown.classList.remove('active');             }          };          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('focus', showAutocomplete);            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              showAutocomplete();              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  if (this.getViewMode() === 'savings_squad') {                    this.activeDealTag = null;                    this.currentQuery = '';                    if (this.categoryFilter) this.categoryFilter.value = 'all';                    this.fetchDeals('');                  } else {                    this.deals = [];                    this.render();                  }                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                                let isTag = false;                if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;                if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;                this.activeDealTag = isTag ? query : null;                                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   if (query.length === 0 && this.getViewMode() === 'savings_squad') {                       if (this.categoryFilter) this.categoryFilter.value = 'all';                   }                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   if (this.categoryFilter && this.airedaleTags.includes(tag)) {                       this.categoryFilter.value = tag;                   }                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                            let isTag = false;              if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;              if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;              this.activeDealTag = isTag ? query : null;                            this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 if (query.length === 0 && this.getViewMode() === 'savings_squad') {                     if (this.categoryFilter) this.categoryFilter.value = 'all';                 }                 this.fetchDeals(query);              }            });          }          if(this.sortSelect && this.sortSelect.querySelector('option[value="date_desc"]') === null) {              const option = document.createElement('option');              option.value = "date_desc";              option.text = "Newest First";              this.sortSelect.insertBefore(option, this.sortSelect.firstChild);          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               if (val) {                 this.currentQuery = val;               } else {                 if (this.searchInput && this.currentQuery === document.querySelector('#tg-df-brand-trigger')?.getAttribute('data-active-brand')) {                     // don't clear current query if a brand is selected                 } else if (this.searchInput) {                     this.currentQuery = '';                     this.searchInput.value = '';                 }               }               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = 'date_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }        async fetchDeals(query) {          this.showLoading();          this.deals = [];          this.displayLimit = this.getRowsLimit();                    try {            console.log("getViewMode returns:", this.getViewMode());            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad();            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query);               } else {                 await this.fetchHawkDeals(query);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const siteParam = this.website || 'tomsguide';            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=${siteParam}&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=${siteParam}&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (!this.activeDealTag && this.currentQuery) {             const tagMatch = this.airedaleTags.find(t => t.toLowerCase() === this.currentQuery.toLowerCase());             if (tagMatch) {                this.activeDealTag = tagMatch;             }          }          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const siteParam = this.website || 'tomsguide';             const url = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=${siteParam}&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let seenUrls = new Set();                    let overallBrandsCounts = {};                    // First pass: extract ALL brands from topArticles so the dropdown has all options          topArticles.forEach((article) => {             if (!article.articlepage) return;             let pageData = [];             try { pageData = JSON.parse(article.articlepage[0]); } catch(e){ console.warn(e); }             const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');             savingsSquad.forEach((block) => {                const data = block.data || {};                if (data.brand) {                   const cleanBrand = data.brand.replace(/^\d+\.\s*/, '').trim();                   overallBrandsCounts[cleanBrand] = (overallBrandsCounts[cleanBrand] || 0) + 1;                }             });          });          targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                if (externalUrl) {                   if (seenUrls.has(externalUrl)) return;                  seenUrls.add(externalUrl);                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || '',                   authorName: article.articleauthortext ? article.articleauthortext[0] : (article.articleauthor ? article.articleauthor[0] : ''),                   authorRole: article.articleauthorrole ? article.articleauthorrole[0] : '',                   authorImage: article.articleauthormedia ? article.articleauthormedia[0] : '',                   documentUrl: article.documenturl ? article.documenturl[0] : '',                   modifiedDate: article.contentmodifieddate || article.modifieddate || ''                });             });          });                    const airedaleBrandsList = Object.keys(overallBrandsCounts).map(b => ({              formatted_value: b,              count: overallBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : 'date_desc';          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                ${_div}              ${_div}              \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin:0; border-radius:0;">${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)}${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = 'View Deal';              if (isSavingsSquadMode) {                priceGroupHtml = ``;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = isSavingsSquadMode ? `` : `                \x3Cdiv class="tg-df-card-price-group">                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                          \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                          \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                            var c = this.parentNode;                            if (this.dataset.expanded === 'true') {                              var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                              if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                              pd.style.display = '-webkit-box';                              pd.style.webkitLineClamp = '3';                              this.textContent = 'READ MORE';                              this.style.position = 'absolute';                              this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                              this.style.paddingLeft = '16px';                              this.dataset.expanded = 'false';                            } else {                              var pd = this.previousElementSibling;                              pd.style.display = 'inline';                              pd.style.webkitLineClamp = 'unset';                              this.textContent = 'READ LESS';                              this.style.position = 'static';                              this.style.background = 'transparent';                              this.style.paddingLeft = '4px';                              this.dataset.expanded = 'true';                              pd.appendChild(this);                            }                          ">READ MORE${_button}                        \x3C/div>` : ''}                      ${_div}                    ${_div}                    ${deal.authorName ? `                      \x3Cdiv class="tg-df-author-line-mobile" style="padding: 0 0 12px 0; background: transparent;">                         \x3Cdiv style="display: flex; align-items: center; gap: 12px;">                            ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="40" height="40" style="border-radius: 50%; object-fit: cover; flex-shrink: 0; aspect-ratio: 1 / 1; min-width: 40px; min-height: 40px;">` : ''}                            \x3Cdiv style="display: flex; flex-direction: column;">                               \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">\x3Cspan style="color: #FF6600;">${this.escapeHTML(deal.merchant)}${_span} deal recommended by:${_div}                               \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.3;">                                  \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                  ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                  ${deal.modifiedDate ? `\x3Cdiv style="color: var(--tg-df-text-muted); margin-top: 2px;">${getTimeAgo(deal.modifiedDate)}${_div}` : ''}                               ${_div}                            ${_div}                         ${_div}                      ${_div}                    ` : ''}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>View Deal${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                  \x3Cdiv class="tg-df-card-merchant-wrapper" style="position: absolute; bottom: 0; right: 0; background: transparent; padding: 8px 12px; z-index: 10;">                     \x3Cspan class="tg-df-card-merchant-pill" style="text-align: right; margin-bottom: 0;" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cdiv class="tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                    \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                    \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                      var c = this.parentNode;                      if (this.dataset.expanded === 'true') {                        var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                        if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                        pd.style.display = '-webkit-box';                        pd.style.webkitLineClamp = '3';                        this.textContent = 'READ MORE';                        this.style.position = 'absolute';                        this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                        this.style.paddingLeft = '16px';                        this.dataset.expanded = 'false';                      } else {                        var pd = this.previousElementSibling;                        pd.style.display = 'inline';                        pd.style.webkitLineClamp = 'unset';                        this.textContent = 'READ LESS';                        this.style.position = 'static';                        this.style.background = 'transparent';                        this.style.paddingLeft = '4px';                        this.dataset.expanded = 'true';                        pd.appendChild(this);                      }                    ">READ MORE${_button}                  \x3C/div>` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${deal.authorName ? `                    \x3Cdiv class="tg-df-author-line-desktop" style="padding: 0 0 ${isSavingsSquadMode ? 0 : 12}px 0;">                       \x3Cdiv style="display: flex; align-items: center; gap: 10px;">                          ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="36" height="36" style="border-radius: 50%; object-fit: cover; flex-shrink: 0; aspect-ratio: 1 / 1; min-width: 36px; min-height: 36px;">` : ''}                          \x3Cdiv style="display: flex; flex-direction: column;">                             \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">Recommended by:${_div}                             \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.2;">                                \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                ${deal.modifiedDate ? `\x3Cspan style="color: var(--tg-df-text-muted);"> • ${getTimeAgo(deal.modifiedDate)}${_span}` : ''}                             ${_div}                          ${_div}                       ${_div}                    ${_div}                    ` : ''}                    ${priceGroupHtml}                  ${_div}                ${_div}                \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta" style="text-decoration: none; border-radius: 0;">${ctaText}${_a}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit) {            dealsHtml += `              \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer;">Load More${_button}              ${_div}            `;          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }          setTimeout(() => {            const contents = this.root.querySelectorAll('.tg-df-card-desc-content');            contents.forEach(p => {              if (p.scrollHeight > p.clientHeight || p.scrollHeight > 60) {                if (p.nextElementSibling) {                  p.nextElementSibling.style.display = 'block';                }              }            });                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');            document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));          }, 50);          const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              this.displayLimit += this.getRowsLimit();              this.render();            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/airpods/apple-airpods-5-preorders-heres-how-to-get-yours-today</link>
                                                                            <description>
                            <![CDATA[ Apple AirPods 5 preorders just went live. Here's what the new AirPods include and how you can preorder them ahead of their September 18 release. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">E9AFVes7DrEb93Y4DTqfcZ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/mZUwbqaZFuaJduw3CHC3wk-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 09 Sep 2026 20:51:57 +0000</pubDate>                                                                                                                                <updated>Fri, 11 Sep 2026 19:43:00 +0000</updated>
                                                                                                                                            <category><![CDATA[Airpods]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ louis.ramirez@futurenet.com (Louis Ramirez) ]]></author>                    <dc:creator><![CDATA[ Louis Ramirez ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oYF2xVwCb4pTGhAJi8Yqr5.jpeg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;As deals editor-in-chief at Tom’s Guide, Louis refuses to pay full price for anything. He has over 10 years of experience finding the best deals and coupons to bring readers. Louis price checks against multiple retailers and searches high and low for sales on 4K TVs, Apple devices, streaming services, mattresses, kitchen appliances, and more. He&#039;s also always looking for the the best coupon codes to turn good deals into stellar buys.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;He has covered major retail events like Amazon Prime Day, Black Friday, and Cyber Monday for more than a decade and knows how to spot an epic deal from a fake discount. He&#039;s also great at helping people negotiate better prices on everything from gym memberships to their home internet service. Louis&#039; work has also helped readers find PS5 restock, baby formula, Clorox wipes, and everything in between. For his next major purchase, Louis is hoping to find a killer deal on an M3-based Mac. &lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;A born-and-bred New Yorker, Louis is always in search of the perfect cup of coffee. On his free time, he enjoys swimming, running, and any workout that pushes his Apple Watch to the max. He&#039;s also a big fan of international films, horror movies, and K-dramas. His work has appeared on Gizmodo, CNET, Digital Trends, Dealnews, and Time Out New York.&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/mZUwbqaZFuaJduw3CHC3wk-1280-80.jpg">
                                                            <media:credit><![CDATA[Apple]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[AirPods 5]]></media:description>                                                            <media:text><![CDATA[AirPods 5]]></media:text>
                                <media:title type="plain"><![CDATA[AirPods 5]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/mZUwbqaZFuaJduw3CHC3wk-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The AirPods family just got bigger. Apple officially unveiled its <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-5-officially-announced-here-are-the-upgrades">AirPods 5</a> at its "Surprise and Shine" keynote. And whereas most of the devices announced at the keynote included higher prices, the AirPods 5 are actually cheaper than their predecessors. </p><p>AirPods 5 preorders are available now <a href="https://www.amazon.com/dp/B0HJB69GJL" target="_blank" rel="nofollow">for $129 with ANC at Amazon</a> or <a href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" rel="nofollow">$149 with a Wireless Charging Case at Amazon</a>. That's cheaper than the AirPods 4 which cost $129 without ANC and $179 with ANC.  </p><p>The AirPods 5 feature the same H3 Wireless Chip found in previous models, but usher in live translation, full support for Siri AI, and 50% more noise reduction than the previous generation. They'll be available in stores on September 18. </p><p>Currently, there are no AirPods 5 deals available. However, it's only a matter of time before a retailer like Amazon or Best Buy knocks $10 off. (For more Apple deals, make sure to check out our <a href="https://www.tomsguide.com/coupons/apple.com">Apple Store coupons</a> coverage).  </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links</span></h3><ul><li><strong>AirPods 5 w/ ANC: </strong><a href="https://www.amazon.com/dp/B0HJB69GJL" target="_blank" rel="nofollow"><strong>for $129 @ Amazon</strong></a><strong></strong></li><li><strong>AirPods 5 w/ Wireless Charging Case: </strong><a href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" data-rewrite="keep"><strong>for $149 @ Amazon</strong></a></li><li><strong>AirPods 5 w/ ANC: </strong><a href="https://www.walmart.com/ip/Apple-AirPods-5/20951672018" target="_blank" rel="nofollow"><strong>for $129 @ Walmart</strong></a><strong></strong></li><li><strong>AirPods 5 w/ Wireless Charging Case: </strong><a href="https://www.walmart.com/ip/Apple-AirPods-5-with-Wireless-Charging-Case/20991207083" target="_blank" rel="nofollow"><strong>for $149 @ Walmart</strong></a></li><li><strong>AirPods 5 w/ ANC: </strong><a href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-white/JJGCQP68Y2/sku/6376553" target="_blank" rel="nofollow"><strong>for $129 @ Best Buy</strong></a></li><li><strong>AirPods 5 w/ Wireless Charging Case: </strong><a href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-with-wireless-charging-case-white/JJGCQP634G/sku/6376564" target="_blank" rel="nofollow"><strong>for $149 @ Best Buy</strong></a></li><li><strong>AirPods 5 w/ ANC: </strong><a href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow"><strong>for $129 @ Apple</strong></a></li><li><strong>AirPods 5 w/ Wireless Charging Case: </strong><a href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow"><strong>for $149 @ Apple</strong></a></li></ul><h3 class="article-body__section" id="section-apple-airpods-5-preorders"><span>Apple AirPods 5 preorders</span></h3><div class="product"><a data-dimension112="b1a198b6-ac8b-11f1-af35-79a83f1df88a" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129" href="https://www.amazon.com/dp/B0HJB69GJL" 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:94.40%;"><img id="mJGKbeqw2FwvrHngHh7KUc" name="Apple AirPods 5 deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/mJGKbeqw2FwvrHngHh7KUc.jpg" mos="" align="middle" fullscreen="" width="1500" height="1416" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0HJB69GJL" target="_blank" rel="nofollow" data-dimension112="b1a198b6-ac8b-11f1-af35-79a83f1df88a" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">AirPods 5 w/ ANC: $129</a></strong><br>Amazon is now offering preorders of the Apple AirPods 5 with ANC. They feature improved noise cancellation, better sound/call quality, Live Translation, and hands-free access to Siri AI. They offer 4 hours of battery life on a single charge. They will be released on September 18. <a class="view-deal button" href="https://www.amazon.com/dp/B0HJB69GJL" target="_blank" rel="nofollow" data-dimension112="b1a198b6-ac8b-11f1-af35-79a83f1df88a" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="e6be4880-ad23-11f1-aad4-6510a933cb60" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149" href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1116px;"><p class="vanilla-image-block" style="padding-top:134.41%;"><img id="UzZQpdMReHRGTHi5gnLv89" name="AirPods 5 w: Wireless Charging Case deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/UzZQpdMReHRGTHi5gnLv89.jpg" mos="" align="middle" fullscreen="" width="1116" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" rel="nofollow" data-dimension112="e6be4880-ad23-11f1-aad4-6510a933cb60" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">AirPods 5 w/ Wireless Charging Case: $149</a></strong><br>Amazon is now offering preorders of the Apple AirPods 5 with Wireless Charging Case. They feature improved noise cancellation, better sound/call quality, Live Translation, and hands-free access to Siri AI. The upgraded model also offers 5 hours of battery life on a single charge and a touch-sensitive stem for controlling music. They will be released on September 18. <a class="view-deal button" href="https://www.amazon.com/dp/B0HJB76H2V" target="_blank" rel="nofollow" data-dimension112="e6be4880-ad23-11f1-aad4-6510a933cb60" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b1a19a1e-ac8b-11f1-ba83-2958700e75cf" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129" href="https://www.walmart.com/ip/Apple-AirPods-5/20951672018" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:960px;"><p class="vanilla-image-block" style="padding-top:93.65%;"><img id="Bi2iDjTxUHTUQK9FHZ4W8D" name="Walmart logo.jpeg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Bi2iDjTxUHTUQK9FHZ4W8D.jpeg" mos="" align="middle" fullscreen="" width="960" height="899" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.walmart.com/ip/Apple-AirPods-5/20951672018" target="_blank" rel="nofollow" data-dimension112="b1a19a1e-ac8b-11f1-ba83-2958700e75cf" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">AirPods 5 w/ ANC: $129</a></strong><br>The new Apple AirPods 5 are now available for preorder at Walmart. Selling for $129 the improved AirPods will ship on September 18. <a class="view-deal button" href="https://www.walmart.com/ip/Apple-AirPods-5/20951672018" target="_blank" rel="nofollow" data-dimension112="b1a19a1e-ac8b-11f1-ba83-2958700e75cf" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="fef55146-ae18-11f1-b3b6-3715ddf4cf4a" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149" href="https://www.walmart.com/ip/Apple-AirPods-5-with-Wireless-Charging-Case/20991207083" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:960px;"><p class="vanilla-image-block" style="padding-top:93.65%;"><img id="Bi2iDjTxUHTUQK9FHZ4W8D" name="Walmart logo.jpeg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Bi2iDjTxUHTUQK9FHZ4W8D.jpeg" mos="" align="middle" fullscreen="" width="960" height="899" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.walmart.com/ip/Apple-AirPods-5-with-Wireless-Charging-Case/20991207083" target="_blank" rel="nofollow" data-dimension112="fef55146-ae18-11f1-b3b6-3715ddf4cf4a" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">AirPods 5 w/ Wireless Charging Case: $149</a></strong><br>The new Apple AirPods 5 are now available for preorder at Walmart. In addition to a wireless charging case, this upgraded model also offers 5 hours of battery life on a single charge and a touch-sensitive stem for controlling music. <a class="view-deal button" href="https://www.walmart.com/ip/Apple-AirPods-5-with-Wireless-Charging-Case/20991207083" target="_blank" rel="nofollow" data-dimension112="fef55146-ae18-11f1-b3b6-3715ddf4cf4a" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b1a1996a-ac8b-11f1-b7da-4da6f45689a1" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129" href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-white/JJGCQP68Y2/sku/6376553" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1331px;"><p class="vanilla-image-block" style="padding-top:68.52%;"><img id="yjsgCbZzcWDLMuhHtLHa7K" name="Best Buy deal block 2022.jpeg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yjsgCbZzcWDLMuhHtLHa7K.jpeg" mos="" align="middle" fullscreen="" width="1331" height="912" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-white/JJGCQP68Y2/sku/6376553" target="_blank" rel="nofollow" data-dimension112="b1a1996a-ac8b-11f1-b7da-4da6f45689a1" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">AirPods 5 w/ ANC: $129</a></strong><br>The Apple AirPods 5 are now available at Best Buy. The base model includes active noise cancellation, Live Translation, and hands-free access to Siri AI. They offer 4 hours of battery life on a single charge. <a class="view-deal button" href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-white/JJGCQP68Y2/sku/6376553" target="_blank" rel="nofollow" data-dimension112="b1a1996a-ac8b-11f1-b7da-4da6f45689a1" data-action="Deal Block" data-label="AirPods 5 w/ ANC" data-dimension48="AirPods 5 w/ ANC" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="fef551dc-ae18-11f1-9469-bf0fbb03a301" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149" href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-with-wireless-charging-case-white/JJGCQP634G/sku/6376564" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1331px;"><p class="vanilla-image-block" style="padding-top:68.52%;"><img id="yjsgCbZzcWDLMuhHtLHa7K" name="Best Buy deal block 2022.jpeg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yjsgCbZzcWDLMuhHtLHa7K.jpeg" mos="" align="middle" fullscreen="" width="1331" height="912" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-with-wireless-charging-case-white/JJGCQP634G/sku/6376564" target="_blank" rel="nofollow" data-dimension112="fef551dc-ae18-11f1-9469-bf0fbb03a301" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">AirPods 5 w/ Wireless Charging Case: $149</a></strong><br>The Apple AirPods 5 with Wireless Charging Case have all the same features as the base model, but also a wireless charging case, 5 hours of battery life on a single charge, and a touch-sensitive stem for controlling music. <a class="view-deal button" href="https://www.bestbuy.com/product/apple-airpods-5-true-wireless-active-noise-cancelling-earbuds-with-wireless-charging-case-white/JJGCQP634G/sku/6376564" target="_blank" rel="nofollow" data-dimension112="fef551dc-ae18-11f1-9469-bf0fbb03a301" data-action="Deal Block" data-label="AirPods 5 w/ Wireless Charging Case" data-dimension48="AirPods 5 w/ Wireless Charging Case" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="b1a19820-ac8b-11f1-b2eb-ed8a3f2d7638" data-action="Deal Block" data-label="AirPods 5" data-dimension48="AirPods 5" data-dimension25="$129" href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:600px;"><p class="vanilla-image-block" style="padding-top:66.67%;"><img id="4Y3oAjs2dPF6xp4nhVsVcc" name="Applelogo.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4Y3oAjs2dPF6xp4nhVsVcc.jpg" mos="" align="middle" fullscreen="" width="600" height="400" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods 5 are available for pre-order at Apple today. They offer live translation, full support for Siri AI, and 50% more noise reduction than the previous generation. They're also slightly cheaper than their predecessor with the ANC model selling for $129 and the AirPods 5 with Wireless Charging Case for $149. You can get your AirPods 5 engraved and personalized for free when you preorder them. Plus, Apple is also giving you three months of Apple Music with your purchase. <a class="view-deal button" href="https://www.apple.com/shop/buy-airpods/airpods-5/without-wireless-charging-case" target="_blank" rel="nofollow" data-dimension112="b1a19820-ac8b-11f1-b2eb-ed8a3f2d7638" data-action="Deal Block" data-label="AirPods 5" data-dimension48="AirPods 5" data-dimension25="$129">View Deal</a></p></div><h2 id="apple-airpods-5-what-39-s-new">Apple AirPods 5 — what's new?</h2><p>The AirPods 5 have an open ear fit with next-gen adaptive EQ. You also get better Siri features, real-time Live Translation, and a durable design that can withstand rain. In terms of battery life, you'll get up to 4 hours with ANC or up to 20 hours with charging case. The upgraded AirPods 5 model has a touch sensitive stem that offers 5 hours of battery with ANC and comes with a wireless charging case, all priced at $149. </p><p>Apple claims they remove up to 50% more noise than the AirPods 4 with ANC thanks to the new multiport acoustic architecture and improved computational audio algorithms. There's also a new Transparency mode that more naturally represents voices and nearby sounds, helping users stay connected to their surroundings.</p><h3 class="article-body__section" id="section-shop-more-deals"><span>Shop more deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?brands=Apple&rows=6&view_mode=savings_squad&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function getTimeAgo(dateString) {        if (!dateString) return '';        const date = new Date(dateString);        const now = new Date();        const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);        if (diffInSeconds < 60) return 'Just now';        const diffInMinutes = Math.floor(diffInSeconds / 60);        if (diffInMinutes < 60) return `${diffInMinutes} min${diffInMinutes > 1 ? 's' : ''} ago`;        const diffInHours = Math.floor(diffInMinutes / 60);        if (diffInHours < 24) return `${diffInHours} hr${diffInHours > 1 ? 's' : ''} ago`;        const diffInDays = Math.floor(diffInHours / 24);        if (diffInDays < 30) return `${diffInDays} day${diffInDays > 1 ? 's' : ''} ago`;        const diffInMonths = Math.floor(diffInDays / 30);        if (diffInMonths < 12) return `${diffInMonths} mo${diffInMonths > 1 ? 's' : ''} ago`;        const diffInYears = Math.floor(diffInDays / 365);        return `${diffInYears} yr${diffInYears > 1 ? 's' : ''} ago`;      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;      position: relative;      z-index: 20;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      margin: 0 auto;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters-container {      position: relative;      width: 100%;      max-width: 800px;    }    .tg-df-scroll-btn {      display: none;      position: absolute;      top: 50%;      transform: translateY(-50%);      width: 32px;      height: 32px;      background: white;      border: 1px solid var(--tg-df-border);      border-radius: 50%;      align-items: center;      justify-content: center;      cursor: pointer;      z-index: 10;      box-shadow: 0 2px 8px rgba(0,0,0,0.1);      color: var(--tg-df-text-primary);      padding: 0;    }    .tg-df-scroll-btn svg {      width: 16px;      height: 16px;    }    .tg-df-scroll-btn:hover {      background: #f4f4f4;    }    .tg-df-scroll-btn.left {      left: 0px;    }    .tg-df-scroll-btn.right {      right: 0px;    }    @container tg-df (max-width: 768px) {      .tg-df-scroll-btn {        display: flex;        top: 22px; /* vertically center within the 44px high filter buttons */      }    }    .tg-df-filters {      display: grid;      width: 100%;      grid-template-columns: repeat(4, 1fr);      gap: 12px;      margin: 0 auto;      max-width: 800px;    }                 .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;      width: 100%;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      width: 100%;      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }    @container tg-df (max-width: 768px) {      .tg-df-filters-container {      position: relative;      width: 100%;      max-width: 800px;    }    .tg-df-scroll-btn {      display: none;      position: absolute;      top: 50%;      transform: translateY(-50%);      width: 32px;      height: 32px;      background: white;      border: 1px solid var(--tg-df-border);      border-radius: 50%;      align-items: center;      justify-content: center;      cursor: pointer;      z-index: 10;      box-shadow: 0 2px 8px rgba(0,0,0,0.1);      color: var(--tg-df-text-primary);      padding: 0;    }    .tg-df-scroll-btn svg {      width: 16px;      height: 16px;    }    .tg-df-scroll-btn:hover {      background: #f4f4f4;    }    .tg-df-scroll-btn.left {      left: 0px;    }    .tg-df-scroll-btn.right {      right: 0px;    }    @container tg-df (max-width: 768px) {      .tg-df-scroll-btn {        display: flex;        top: 22px; /* vertically center within the 44px high filter buttons */      }    }    .tg-df-filters {        width: 100%;        margin: 0;        margin-bottom: -320px;        padding: 0 16px 320px 16px;        display: flex;        flex-wrap: nowrap;        gap: 8px;        overflow-x: auto;        overflow-y: hidden;        pointer-events: none;        scrollbar-width: none;        -webkit-overflow-scrolling: touch;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }      .tg-df-sort-wrapper {        pointer-events: auto;        flex: 0 0 auto;        width: 175px;        min-width: 175px;      }    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 120px;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 16px 16px 8px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }                  .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;        width: auto;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;      width: 100vw;      max-width: 1200px;      position: relative;      left: 50%;      transform: translateX(-50%);    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 14px;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    .tg-df-grid-wrapper { position: relative; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn {      display: inline-flex;      align-items: center;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.40);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #1f69ff; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #0056e0; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }    .tg-df-savings-squad-header { margin-bottom: 24px; text-align: center; display: none; }    .tg-df-banner-img-desktop { display: block; width: 100%; height: auto; margin-bottom: 32px; }    .tg-df-banner-img-mobile { display: none; width: 100%; height: auto; margin-bottom: 32px; }    @container tg-df (max-width: 600px) {      .tg-df-banner-img-desktop { display: none; }      .tg-df-banner-img-mobile { display: block; }    }    .tg-df-header-title { font-size: 28px; font-weight: 700; color: var(--tg-df-text); margin: 32px 0 12px 0; line-height: 1.3; }    .tg-df-header-subtitle { font-size: 16px; color: var(--tg-df-text-muted); margin: 0 0 32px 0; line-height: 1.5; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3Cdiv class="tg-df-savings-squad-header" id="tg-df-savings-squad-header">      \x3Cpicture>        \x3Cimg src="https://cdn.mos.cms.futurecdn.net/flexiimages/xkh2og7m3d1778189998.png" alt="Deals Banner" class="tg-df-banner-img-desktop" />        \x3Cimg src="https://cdn.mos.cms.futurecdn.net/flexiimages/gmak6rtdf41778245089.png" alt="Deals Banner Mobile" class="tg-df-banner-img-mobile" />      \x3C/picture>      \x3Cdiv class="tg-df-header-text">        \x3Ch2 class="tg-df-header-title" id="tg-df-header-title">Editor's Choice Deals\x3C/h2>        \x3Cp class="tg-df-header-subtitle" id="tg-df-header-subtitle">Discover the best discounts currently available, curated daily by the Tom's Guide Savings Squad.\x3C/p>      \x3C/div>    \x3C/div>    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Televisions" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/wcMxTsHgqu3roMbAx7RLnT-132-100.png" alt="TVs" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">TVs\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Phones" data-pr="over50">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/G3KGaRGzj24F6PUsw4bWpT-132-100.png" alt="Phones" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Phones\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Computing" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/znNvsLzx8NEgNkD9HSFSnT-132-100.png" alt="Computing" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Computing\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Gaming" data-pr="all">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/Pgew8yaRQeZFHqHjTzvBnT-132-100.png" alt="Gaming" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Gaming\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Mattresses" data-pr="over500">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/cW7xsaLyesxkHFVSiC4kmT-132-100.png" alt="Mattresses" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Mattresses\x3C/span>            \x3C/div>                      \x3Cdiv class="tg-df-roundel tg-df-carousel-cat" data-query="Audio" data-pr="over30">              \x3Cdiv class="tg-df-roundel-img-box">                 \x3Cimg src="https://cdn.mos.cms.futurecdn.net/pCvBVHuhaQVjKt3VgCjbqT-132-100.png" alt="Audio" />              \x3C/div>              \x3Cspan class="tg-df-roundel-label">Audio\x3C/span>            \x3C/div>                  \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E      \x3Cdiv class="tg-df-top-bar" id="tg-df-top-bar" style="position: relative; z-index: 100; margin: 0 auto 20px;">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>      \x3C/div>    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">              \x3Cdiv class="tg-df-filters-container" style="position: relative; width: 100%; max-width: 800px; margin: 0 auto;">          \x3Cbutton class="tg-df-scroll-btn left" style="display: none;" onclick="this.parentElement.querySelector('.tg-df-filters').scrollBy({left: -200, behavior: 'smooth'})">            \x3Csvg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\x3Cpath d="M15 18l-6-6 6-6"/>\x3C/svg>          \x3C/button>          \x3Cbutton class="tg-df-scroll-btn right" style="display: none;" onclick="this.parentElement.querySelector('.tg-df-filters').scrollBy({left: 200, behavior: 'smooth'})">            \x3Csvg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">\x3Cpath d="M9 18l6-6-6-6"/>\x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-filters">          \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>          \x3C/div>        \x3C/div>        \x3C/div>      \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();        }        getViewMode() {          console.log("DEBUG getViewMode -> override:", this.viewModeOverride, "editorMode:", this.editorMode);          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          console.log("[DEBUG] applyLayoutMode CALLED! mode=", mode);          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          const showHeaderDetails = params.get('show_header_details') !== 'false';          const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          const topBarDiv = this.root.querySelector('#tg-df-top-bar');          const headerElement = this.root.querySelector('#tg-df-savings-squad-header');          if (headerElement) {             headerElement.style.display = (mode === 'savings_squad' && showHeaderDetails) ? 'block' : 'none';          }          if (mode === 'carousel') {             this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (topBarDiv) topBarDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.add('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (topBarDiv) topBarDiv.style.display = 'block';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!this.viewModeOverride && initialViewMode) {            this.viewModeOverride = initialViewMode;          }          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Gaming laptops';          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (this.viewModeSelect && params.has('view_mode')) {            this.viewModeSelect.value = params.get('view_mode');          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'date_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }          const headerTitleEl = this.root.querySelector('#tg-df-header-title');          const headerSubtitleEl = this.root.querySelector('#tg-df-header-subtitle');          if (params.has('widget_title') && headerTitleEl) {             headerTitleEl.textContent = params.get('widget_title');          }          if (params.has('widget_subtitle') && headerSubtitleEl) {             headerSubtitleEl.textContent = params.get('widget_subtitle');          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            if (this.getViewMode() === 'savings_squad') {              this.searchInput.value = '';            } else {              this.searchInput.value = this.currentQuery;            }          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'date_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const handleFiltersScroll = () => {            const filters = this.root.querySelector('.tg-df-filters');            const leftBtn = this.root.querySelector('.tg-df-scroll-btn.left');            const rightBtn = this.root.querySelector('.tg-df-scroll-btn.right');            if (filters && leftBtn && rightBtn) {              const { scrollLeft, scrollWidth, clientWidth } = filters;              leftBtn.style.display = scrollLeft > 0 ? 'flex' : 'none';              rightBtn.style.display = Math.ceil(scrollLeft + clientWidth) < scrollWidth - 5 ? 'flex' : 'none';            }          };          const filters = this.root.querySelector('.tg-df-filters');          if (filters) {            filters.addEventListener('scroll', handleFiltersScroll);            window.addEventListener('resize', handleFiltersScroll);            setTimeout(handleFiltersScroll, 100);                        // Also call after rendering dropdowns            const origRenderCategories = this.renderCategories;            if (origRenderCategories) {               this.renderCategories = (...args) => {                 origRenderCategories.apply(this, args);                 setTimeout(handleFiltersScroll, 50);               };            }          }                const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          const showAutocomplete = () => {             if (this.getViewMode() !== 'savings_squad' || !this.autocompleteDropdown || !this.airedaleTags) return;                          let terms = this.airedaleTags;             if (this.airedaleBrands) {                terms = terms.concat(this.airedaleBrands.map(b => b.formatted_value));             }             terms = [...new Set(terms)];                          const query = this.searchInput.value.trim();             let matches = [];             if (query.length > 0) {                 matches = terms.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase());             } else {                 matches = terms;             }                          if (matches.length > 0) {                 this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                 this.autocompleteDropdown.classList.add('active');             } else {                 this.autocompleteDropdown.classList.remove('active');             }          };          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('focus', showAutocomplete);            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              showAutocomplete();              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  if (this.getViewMode() === 'savings_squad') {                    this.activeDealTag = null;                    this.currentQuery = '';                    if (this.categoryFilter) this.categoryFilter.value = 'all';                    this.fetchDeals('');                  } else {                    this.deals = [];                    this.render();                  }                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                                let isTag = false;                if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;                if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;                this.activeDealTag = isTag ? query : null;                                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   if (query.length === 0 && this.getViewMode() === 'savings_squad') {                       if (this.categoryFilter) this.categoryFilter.value = 'all';                   }                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   if (this.categoryFilter && this.airedaleTags.includes(tag)) {                       this.categoryFilter.value = tag;                   }                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                            let isTag = false;              if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;              if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;              this.activeDealTag = isTag ? query : null;                            this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 if (query.length === 0 && this.getViewMode() === 'savings_squad') {                     if (this.categoryFilter) this.categoryFilter.value = 'all';                 }                 this.fetchDeals(query);              }            });          }          if(this.sortSelect && this.sortSelect.querySelector('option[value="date_desc"]') === null) {              const option = document.createElement('option');              option.value = "date_desc";              option.text = "Newest First";              this.sortSelect.insertBefore(option, this.sortSelect.firstChild);          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               if (val) {                 this.currentQuery = val;               } else {                 if (this.searchInput && this.currentQuery === document.querySelector('#tg-df-brand-trigger')?.getAttribute('data-active-brand')) {                     // don't clear current query if a brand is selected                 } else if (this.searchInput) {                     this.currentQuery = '';                     this.searchInput.value = '';                 }               }               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = 'date_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }        async fetchDeals(query) {          this.showLoading();          this.deals = [];          this.displayLimit = this.getRowsLimit();                    try {            console.log("getViewMode returns:", this.getViewMode());            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad();            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query);               } else {                 await this.fetchHawkDeals(query);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const siteParam = this.website || 'tomsguide';            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=${siteParam}&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=${siteParam}&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (!this.activeDealTag && this.currentQuery) {             const tagMatch = this.airedaleTags.find(t => t.toLowerCase() === this.currentQuery.toLowerCase());             if (tagMatch) {                this.activeDealTag = tagMatch;             }          }          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const siteParam = this.website || 'tomsguide';             const url = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=${siteParam}&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let seenUrls = new Set();                    let overallBrandsCounts = {};                    // First pass: extract ALL brands from topArticles so the dropdown has all options          topArticles.forEach((article) => {             if (!article.articlepage) return;             let pageData = [];             try { pageData = JSON.parse(article.articlepage[0]); } catch(e){ console.warn(e); }             const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');             savingsSquad.forEach((block) => {                const data = block.data || {};                if (data.brand) {                   const cleanBrand = data.brand.replace(/^\d+\.\s*/, '').trim();                   overallBrandsCounts[cleanBrand] = (overallBrandsCounts[cleanBrand] || 0) + 1;                }             });          });          targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                if (externalUrl) {                   if (seenUrls.has(externalUrl)) return;                  seenUrls.add(externalUrl);                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || '',                   authorName: article.articleauthortext ? article.articleauthortext[0] : (article.articleauthor ? article.articleauthor[0] : ''),                   authorRole: article.articleauthorrole ? article.articleauthorrole[0] : '',                   authorImage: article.articleauthormedia ? article.articleauthormedia[0] : '',                   documentUrl: article.documenturl ? article.documenturl[0] : '',                   modifiedDate: article.contentmodifieddate || article.modifieddate || ''                });             });          });                    const airedaleBrandsList = Object.keys(overallBrandsCounts).map(b => ({              formatted_value: b,              count: overallBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : 'date_desc';          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                ${_div}              ${_div}              \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin:0; border-radius:0;">${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)}${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = 'View Deal';              if (isSavingsSquadMode) {                priceGroupHtml = ``;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = isSavingsSquadMode ? `` : `                \x3Cdiv class="tg-df-card-price-group">                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                          \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                          \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                            var c = this.parentNode;                            if (this.dataset.expanded === 'true') {                              var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                              if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                              pd.style.display = '-webkit-box';                              pd.style.webkitLineClamp = '3';                              this.textContent = 'READ MORE';                              this.style.position = 'absolute';                              this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                              this.style.paddingLeft = '16px';                              this.dataset.expanded = 'false';                            } else {                              var pd = this.previousElementSibling;                              pd.style.display = 'inline';                              pd.style.webkitLineClamp = 'unset';                              this.textContent = 'READ LESS';                              this.style.position = 'static';                              this.style.background = 'transparent';                              this.style.paddingLeft = '4px';                              this.dataset.expanded = 'true';                              pd.appendChild(this);                            }                          ">READ MORE${_button}                        \x3C/div>` : ''}                      ${_div}                    ${_div}                    ${deal.authorName ? `                      \x3Cdiv class="tg-df-author-line-mobile" style="padding: 0 0 12px 0; background: transparent;">                         \x3Cdiv style="display: flex; align-items: center; gap: 12px;">                            ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="40" height="40" style="border-radius: 50%; object-fit: cover; flex-shrink: 0; aspect-ratio: 1 / 1; min-width: 40px; min-height: 40px;">` : ''}                            \x3Cdiv style="display: flex; flex-direction: column;">                               \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">\x3Cspan style="color: #FF6600;">${this.escapeHTML(deal.merchant)}${_span} deal recommended by:${_div}                               \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.3;">                                  \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                  ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                  ${deal.modifiedDate ? `\x3Cdiv style="color: var(--tg-df-text-muted); margin-top: 2px;">${getTimeAgo(deal.modifiedDate)}${_div}` : ''}                               ${_div}                            ${_div}                         ${_div}                      ${_div}                    ` : ''}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>View Deal${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                  \x3Cdiv class="tg-df-card-merchant-wrapper" style="position: absolute; bottom: 0; right: 0; background: transparent; padding: 8px 12px; z-index: 10;">                     \x3Cspan class="tg-df-card-merchant-pill" style="text-align: right; margin-bottom: 0;" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cdiv class="tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                    \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                    \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                      var c = this.parentNode;                      if (this.dataset.expanded === 'true') {                        var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                        if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                        pd.style.display = '-webkit-box';                        pd.style.webkitLineClamp = '3';                        this.textContent = 'READ MORE';                        this.style.position = 'absolute';                        this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                        this.style.paddingLeft = '16px';                        this.dataset.expanded = 'false';                      } else {                        var pd = this.previousElementSibling;                        pd.style.display = 'inline';                        pd.style.webkitLineClamp = 'unset';                        this.textContent = 'READ LESS';                        this.style.position = 'static';                        this.style.background = 'transparent';                        this.style.paddingLeft = '4px';                        this.dataset.expanded = 'true';                        pd.appendChild(this);                      }                    ">READ MORE${_button}                  \x3C/div>` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${deal.authorName ? `                    \x3Cdiv class="tg-df-author-line-desktop" style="padding: 0 0 ${isSavingsSquadMode ? 0 : 12}px 0;">                       \x3Cdiv style="display: flex; align-items: center; gap: 10px;">                          ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="36" height="36" style="border-radius: 50%; object-fit: cover; flex-shrink: 0; aspect-ratio: 1 / 1; min-width: 36px; min-height: 36px;">` : ''}                          \x3Cdiv style="display: flex; flex-direction: column;">                             \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">Recommended by:${_div}                             \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.2;">                                \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                ${deal.modifiedDate ? `\x3Cspan style="color: var(--tg-df-text-muted);"> • ${getTimeAgo(deal.modifiedDate)}${_span}` : ''}                             ${_div}                          ${_div}                       ${_div}                    ${_div}                    ` : ''}                    ${priceGroupHtml}                  ${_div}                ${_div}                \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta" style="text-decoration: none; border-radius: 0;">${ctaText}${_a}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit) {            dealsHtml += `              \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer;">Load More${_button}              ${_div}            `;          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }          setTimeout(() => {            const contents = this.root.querySelectorAll('.tg-df-card-desc-content');            contents.forEach(p => {              if (p.scrollHeight > p.clientHeight || p.scrollHeight > 60) {                if (p.nextElementSibling) {                  p.nextElementSibling.style.display = 'block';                }              }            });                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');            document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));          }, 50);          const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              this.displayLimit += this.getRowsLimit();              this.render();            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Apple AirPods 5 officially announced — here are the upgrades ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Apple just announced the AirPods 5 alongside a bunch of other products at the <a href="https://www.tomsguide.com/news/live/apple-event-live-foldable-iphone-iphone-18-pro-max-apple-watch-12-and-all-the-big-news">"Surprise and Shine" event</a> in Cupertino, California. </p><p>Packed with new features, the AirPods 5 definitely look like a worthwhile alternative to the latest AirPods Pro 3. </p><p>They have the open fit the previous models were known for, which is great for people who don't like the Pro's tips. Apple says they'll have the best ANC for this type of earbud, which is a big deal.</p><h3 class="article-body__section" id="section-apple-watch-5-quick-info"><span>Apple Watch 5 quick info</span></h3><ul><li><strong>Price: </strong>$129 / AU$219 for the base model with ANC. $149 / AU$249 for the higher-end model with a wireless charging case and volume on the stem.</li><li><strong>Availability: </strong>Announced at the "Surprise and Shine" event; available for pre-order today</li><li><strong>Features</strong>: Live translation, full support for Siri AI, 50% more noise reduction than the previous generation,</li><li><strong>Under-the-hood:</strong> H3 Wireless Chip</li><li><strong>Battery life: </strong>Up to<strong> </strong>4 hours of listening time with ANC on, up to 20 hours with the charging case</li></ul><h3 class="article-body__section" id="section-what-makes-the-airpods-5-stand-out"><span>What makes the AirPods 5 stand out?</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LUzNNNDgoj2eHotY7o8efY" name="Apple Event — September 9 35-9 screenshot" alt="AirPods 5 lifestyle" src="https://cdn.mos.cms.futurecdn.net/LUzNNNDgoj2eHotY7o8efY.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: Apple)</span></figcaption></figure><p>Apple announced some exciting new features for the AirPods 5, but perhaps the most exciting element is the lower price. The base model, now with active noise cancellation, costs only $129 / AU$218. That's the same price as the <a href="https://www.tomsguide.com/audio/headphones/i-tested-the-new-airpods-4-with-noise-canceling-for-a-week-here-are-my-pros-and-cons">AirPods 4 without ANC</a>. </p><p>At the same time, Apple announced an upgraded version, the AirPods 5 with Wireless Charging Case, for $149 AU$249. That's cheaper than the high-end AirPods 4. In today's economy, prices don't drop often, so this is exciting.</p><p>As the name implies, it offers wireless charging, but it also has a force sensor with volume swipe, which the company says is a first for the open-ear form factor.</p><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="VHZMj9yrY2KANUCukzMDvj" name="Apple Event — September 9 36-43 screenshot" alt="On-stem volume control on AirPods 5" src="https://cdn.mos.cms.futurecdn.net/VHZMj9yrY2KANUCukzMDvj.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: Apple)</span></figcaption></figure><p>The other big change for the more expensive model is longer battery life. The cheaper model is rated to last about four hours with ANC on, while the upgraded model should last about five hours.</p><p>Obviously, audio quality is critical for any pair of headphones, and Apple is making some lofty promises. It described a "new multiport acoustic architecture and next-generation <a href="https://www.tomsguide.com/phones/iphones/airpods-finally-get-a-custom-eq-in-ios-27-here-is-how-to-fix-your-sound">Adaptive EQ</a> deliver richer, more detailed sound across an even wider range of ear geometries."</p><p>Apple is also integrating AI features like <a href="https://www.tomsguide.com/audio/airpods/airpods-pro-3-live-translation-heres-how-to-use-it">Live Translation</a> and <a href="https://www.tomsguide.com/ai/apple-finally-fixed-siri-heres-all-the-features-for-the-new-siri-ai-announced-at-wwdc">Siri AI</a>, so you can take advantage of them at a lower price point.</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/buying-guide/best-airpods">Best AirPods: Top Apple models tested and rated</a></li><li><a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-ultra-4-needs-these-3-features-to-compete-with-the-likes-of-the-garmin-fenix-9">Apple Watch Ultra 4 needs these 3 features to compete with the likes of the Garmin Fenix 9</a></li><li><a href="https://www.tomsguide.com/phones/live/apple-surprise-and-shine-live-iphone-ultra-iphone-18-pro-max-apple-watch-series-12-and-all-the-latest-news">Apple September event live blog</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/airpods/apple-airpods-5-officially-announced-here-are-the-upgrades</link>
                                                                            <description>
                            <![CDATA[ We weren't sure if Apple would announce new AirPods, but the company revealed the latest model with some big upgrades. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">hDewjMMffscjTG7GcQ2APn</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/bGcwz5CcYkGkaEMqtgpCEQ-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Wed, 09 Sep 2026 17:33:34 +0000</pubDate>                                                                                                                                <updated>Thu, 10 Sep 2026 00:14:49 +0000</updated>
                                                                                                                                            <category><![CDATA[Airpods]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ dave.leclair@futurenet.com (Dave LeClair) ]]></author>                    <dc:creator><![CDATA[ Dave LeClair ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/fyx7qYdxPMTNBhdnMfNmaB.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Dave LeClair is the Managing News Editor for Tom&#039;s Guide, keeping his finger on the pulse of all things technology. He loves taking the complicated happenings in the tech world and explaining why they matter. Whether Apple is announcing the next big thing in the mobile space or a small startup advancing generative AI, Dave will apply his experience to help you figure out what&#039;s happening and why it&#039;s relevant to your life.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Before Tom&#039;s Guide, Dave worked for publications like PCMag, Pocket-lint, MUO, How-To Geek, Digital Trends, and others. He started writing about technology professionally for MUO in 2011 and hasn&#039;t looked back since. In addition to news, you can find reviews, how-to pieces, shopping guides, and many other types of content with Dave&#039;s name attached.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/png" url="https://cdn.mos.cms.futurecdn.net/bGcwz5CcYkGkaEMqtgpCEQ-1280-80.png">
                                                            <media:credit><![CDATA[Apple]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[AirPods 5]]></media:description>                                                            <media:text><![CDATA[AirPods 5]]></media:text>
                                <media:title type="plain"><![CDATA[AirPods 5]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/bGcwz5CcYkGkaEMqtgpCEQ-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Apple just announced the AirPods 5 alongside a bunch of other products at the <a href="https://www.tomsguide.com/news/live/apple-event-live-foldable-iphone-iphone-18-pro-max-apple-watch-12-and-all-the-big-news">"Surprise and Shine" event</a> in Cupertino, California. </p><p>Packed with new features, the AirPods 5 definitely look like a worthwhile alternative to the latest AirPods Pro 3. </p><p>They have the open fit the previous models were known for, which is great for people who don't like the Pro's tips. Apple says they'll have the best ANC for this type of earbud, which is a big deal.</p><h3 class="article-body__section" id="section-apple-watch-5-quick-info"><span>Apple Watch 5 quick info</span></h3><ul><li><strong>Price: </strong>$129 / AU$219 for the base model with ANC. $149 / AU$249 for the higher-end model with a wireless charging case and volume on the stem.</li><li><strong>Availability: </strong>Announced at the "Surprise and Shine" event; available for pre-order today</li><li><strong>Features</strong>: Live translation, full support for Siri AI, 50% more noise reduction than the previous generation,</li><li><strong>Under-the-hood:</strong> H3 Wireless Chip</li><li><strong>Battery life: </strong>Up to<strong> </strong>4 hours of listening time with ANC on, up to 20 hours with the charging case</li></ul><h3 class="article-body__section" id="section-what-makes-the-airpods-5-stand-out"><span>What makes the AirPods 5 stand out?</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LUzNNNDgoj2eHotY7o8efY" name="Apple Event — September 9 35-9 screenshot" alt="AirPods 5 lifestyle" src="https://cdn.mos.cms.futurecdn.net/LUzNNNDgoj2eHotY7o8efY.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: Apple)</span></figcaption></figure><p>Apple announced some exciting new features for the AirPods 5, but perhaps the most exciting element is the lower price. The base model, now with active noise cancellation, costs only $129 / AU$218. That's the same price as the <a href="https://www.tomsguide.com/audio/headphones/i-tested-the-new-airpods-4-with-noise-canceling-for-a-week-here-are-my-pros-and-cons">AirPods 4 without ANC</a>. </p><p>At the same time, Apple announced an upgraded version, the AirPods 5 with Wireless Charging Case, for $149 AU$249. That's cheaper than the high-end AirPods 4. In today's economy, prices don't drop often, so this is exciting.</p><p>As the name implies, it offers wireless charging, but it also has a force sensor with volume swipe, which the company says is a first for the open-ear form factor.</p><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="VHZMj9yrY2KANUCukzMDvj" name="Apple Event — September 9 36-43 screenshot" alt="On-stem volume control on AirPods 5" src="https://cdn.mos.cms.futurecdn.net/VHZMj9yrY2KANUCukzMDvj.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: Apple)</span></figcaption></figure><p>The other big change for the more expensive model is longer battery life. The cheaper model is rated to last about four hours with ANC on, while the upgraded model should last about five hours.</p><p>Obviously, audio quality is critical for any pair of headphones, and Apple is making some lofty promises. It described a "new multiport acoustic architecture and next-generation <a href="https://www.tomsguide.com/phones/iphones/airpods-finally-get-a-custom-eq-in-ios-27-here-is-how-to-fix-your-sound">Adaptive EQ</a> deliver richer, more detailed sound across an even wider range of ear geometries."</p><p>Apple is also integrating AI features like <a href="https://www.tomsguide.com/audio/airpods/airpods-pro-3-live-translation-heres-how-to-use-it">Live Translation</a> and <a href="https://www.tomsguide.com/ai/apple-finally-fixed-siri-heres-all-the-features-for-the-new-siri-ai-announced-at-wwdc">Siri AI</a>, so you can take advantage of them at a lower price point.</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/buying-guide/best-airpods">Best AirPods: Top Apple models tested and rated</a></li><li><a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-ultra-4-needs-these-3-features-to-compete-with-the-likes-of-the-garmin-fenix-9">Apple Watch Ultra 4 needs these 3 features to compete with the likes of the Garmin Fenix 9</a></li><li><a href="https://www.tomsguide.com/phones/live/apple-surprise-and-shine-live-iphone-ultra-iphone-18-pro-max-apple-watch-series-12-and-all-the-latest-news">Apple September event live blog</a></li></ul>
                                                            </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.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.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.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.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.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.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.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-1280-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.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-1280-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-1280-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.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.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.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.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.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.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.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[ I’ve been testing these sub-$50 open-ear earbuds, and they prove that you don’t have to spend more to get excellent audio gear ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I’m a firm believer that everyone needs a pair of open-ear earbuds in their audio gear rotation, and if you’re still a skeptic, the SoundPEATS UU2 earbuds are the perfect affordable way to give ambient sound with your music a try. </p><p>These earbuds are excellent value for money — at less than $50, they also boast amazing sound performance that I’m more than happy to listen to all day long. They’re comfortable to wear and have button controls which are very responsive. Not to mention the 42 hours of battery life, which is some of the best on the market. </p><p>Unfortunately, there are some things that SoundPEATS could improve on. The UU2’s call quality isn’t the best; there’s some slight movement when wearing the earbuds for high-intensity workouts; and the companion app is clunky. If you’d like to find out more about the buds, you can keep reading this full SoundPEATS UU2 review.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-specs"><span>SoundPEATS UU2 review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://soundpeats.com/en-gb/products/uu2-clip-on-earbuds" target="_blank" rel="nofollow">$49</a> / <a href="https://www.amazon.co.uk/SoundPEATS-UU2-Open-Ear-Earbuds-Physical-Black/dp/B0H4JJRKVY" target="_blank" rel="nofollow">$59</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>1.68 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Buds only weight</strong></p></td><td  ><p>0.18 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Controls</strong></p></td><td  ><p>Buttons</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IPX5</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 6.0</p></td></tr><tr><td class="firstcol " ><p><strong>Supported codecs</strong></p></td><td  ><p>SBC/AAC/LDAC</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>42 hours</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-soundpeats-uu2-review-price-availability"><span>SoundPEATS UU2 review: Price & availability</span></h2><p>The first thing to note is that these headphones have undergone a name change. They were previously named the POP Clip2, but now go by SoundPEATS UU2. The earbuds are available to purchase in the U.S. directly from the <a href="https://soundpeats.com/en-gb/products/uu2-clip-on-earbuds" target="_blank" rel="nofollow">SoundPEATS website for $49</a> and in the U.K. on <a href="https://www.amazon.co.uk/SoundPEATS-UU2-Open-Ear-Earbuds-Physical-Black/dp/B0H4JJRKVY" target="_blank" rel="nofollow">Amazon for £59</a>. </p><p>These are the cheapest open-ear earbuds I’ve tested, but, in all honesty, when I listened to my music with them before knowing the price, I thought they would cost well over $100. But if it’s affordable audio gear you’re looking for, there are a couple of options depending on the fit you want. </p><p>The UU2 are clip-style earbuds, like the <a href="https://www.tomsguide.com/audio/earbuds/earfun-clip-2-review">EarFun Clip 2</a> for $99. I find this the most comfortable style, especially for working out and all-day wear. And the Clip 2 earbuds have features like live translation and impressive call quality, which you don’t get with the UU2s.  You can also get the original <a href="https://www.tomsguide.com/audio/earbuds/earfun-clip-review">EarFun Clip</a> earbuds, which have a great bass performance for $69. This will be better if you like listening to more bass-driven music. </p><p>Alternatively, you could look at a hook design. My favorite budget hook buds are the <a href="https://www.tomsguide.com/audio/earbuds/jlab-epic-open-sport-review">JLab Epic Open Sport</a> for $114. They work best for high-intensity workouts like running, so if you need something that stays secure, these could be the buds for you. However, the sound quality is a mixed bag, and the UU2s offer a more consistent sound.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-design"><span>SoundPEATS UU2 review: Design </span></h2><p>The SoundPEATS UU2 open-earbuds feel premium despite their sub-$50 price tag. They look very similar to the EarFun Clip 2, but have a shinier finish, which I think makes them feel a little more premium. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="G2qurW8saCDG4yGpAmpeDF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/G2qurW8saCDG4yGpAmpeDF.jpg" 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 case is small and fits in the palm of my hand, so it’s easy to slip into my pocket when I’m using the buds. They have an IPX5 durability rating, which means they’re protected from water splashes but not dust. This makes them ideal for walking outdoors in case you get caught out in the rain, but not for jogging on the beach in case you drop them in the sand. This kind of rating is standard for earbuds, but I would like to see some dust protection, as you get with the JLab Epic Open Sport.</p><h3 id="comfort-2">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qj5RokMc4NpVRZLnA8spAF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/qj5RokMc4NpVRZLnA8spAF.jpg" 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>SoundPEATS say that the two Us in the name stand for ‘Ultra Comfort and Ultimate Freedom’; I’d have to agree. I could wear them all day long without them hurting since the buds are so lightweight at 0.18 ounces each. </p><p>When wearing the buds during lighter movement, such as yoga and Pilates, they felt very secure, but there is some slight movement during high-intensity workouts like running or HIIT, so keep this in mind. I found the <a href="https://www.tomsguide.com/audio/earbuds/soundcore-aeroclip-review">Soundcore AeroClip</a> earbuds to be more secure, but they are more expensive at $129.</p><h3 id="controls-3">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="m7urYWDk2YNCP43L5AdxDF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/m7urYWDk2YNCP43L5AdxDF.jpg" 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 SoundPEATS UU2 are controlled using physical buttons. These were responsive and easy to operate, and you can even customize this in the SoundPEATS app, which I’ll talk more about later. I much prefer these controls to touch-sensitive controls, as when you're moving, there’s less chance of a missed command, as it takes more intent to click a button. </p><h2 id="connectivity-3">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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Lxcu99dXLxdPUk5V7acHEF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/Lxcu99dXLxdPUk5V7acHEF.jpg" 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 SoundPEATS UU2 earbuds use the latest (at the time of writing) Bluetooth 6.0, which delivers better power consumption and connection stability. Codec-wise, they support SBC, AAC and LDAC audio codecs, the latter allowing hi-res wireless playback. They also support multipoint connection with up to two devices, which is ideal for when I’m working and need to switch between my phone and laptop for calls. It’s easy to connect multiple devices by using the SoundPEATS app.</p><h3 class="article-body__section" id="section-soundpeats-uu2-review-sound-quality"><span>SoundPEATS UU2 review: Sound quality</span></h3><p>The SoundPEATS UU2 earbuds don’t provide ultimate high-end sound quality — we’re not talking <a href="https://www.tomsguide.com/audio/earbuds/beyerdynamic-amiron-zero-review">Beyerdynamic AMIRON Zero</a> ($179) performance here — but for the price tag, I really can’t ask for any more. They’ve blown me away. I tried out the EQ adjustment settings in the app, but ended up sticking with the default tuning as it was the best for me. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="GydusHNMSMNdkev5WtGzCF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/GydusHNMSMNdkev5WtGzCF.jpg" 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>Firstly, I wanted to listen to the bass performance of the buds, because open-earbuds can often struggle with bass replication since the drivers are outside of your ear. However, I was impressed. I listened to <em>Heads Will Roll </em>by the Yeah Yeah Yeahs, the A-Trak remix, which relies on bass for a heavy club sound, with the vocals adding more structure. The bass was mostly clear, with some very slight distortion when increasing the volume. The synth also sounded great — exactly what I want from this song to feel motivated while working out. </p><p>Next, I listened to <em>Favourite</em> by Fontaines D.C. to test out the mid-range, because of its catchy guitar track and prominent drums. The track was bright and well balanced. I could hear the guitar in detail, and it was punchy and strong. The guitar and drums weren’t overpowering though, and the vocals still shone 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TFzYvYveEUGPzqGv52Pc9F" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/TFzYvYveEUGPzqGv52Pc9F.jpg" 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 the high-frequency performance, I listened to <em>My Tears Ricochet</em> by Taylor Swift. This song uses orchestral elements such as violin and choral harmonies. All of these elements were clear and balanced, leaving space for soft vocals to shine before the song builds into something more powerful. And even then, the earbuds held their own impressively.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-app"><span>SoundPEATS UU2 review: App</span></h2><p>I usually love using an app with earbuds as it adds a layer of customization that makes the listening experience times better. However, I found the SoundPEATS app clunky, especially during the initial setup. With use, it became easier to navigate, but I much prefer apps like the one from Soundcore that are instantly usable. </p><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="3paWsmwN8cHyV9NAeQ58ud" name="soundpeats app" alt="screenshot taken from the SoundPEATS app for the SoundPEATS UU2 earbuds showing the settings" src="https://cdn.mos.cms.futurecdn.net/3paWsmwN8cHyV9NAeQ58ud.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: SoundPEATS)</span></figcaption></figure><p>That said, the app has some useful features. You can enable Spatial audio or make EQ and control adjustments. There’s also a drainage function, which helps expel moisture from inside the earbuds by playing low-frequency tones, effectively shaking out the water. I’m not sure how much this will actually help if you drop your earbuds into a pool for argument's sake, but it’s an interesting feature nonetheless.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-call-quality"><span>SoundPEATS UU2 review: Call quality</span></h2><p>To evaluate the call quality of the SoundPEATS UU2 earbuds, I carried out my usual test and called my mom. She let me know that I didn’t sound as clear as usual. This was disappointing, as these buds are very impressive in pretty much every other area. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="m7urYWDk2YNCP43L5AdxDF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/m7urYWDk2YNCP43L5AdxDF.jpg" 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 pair of open buds for work calls that are still budget-friendly, I’d recommend the Soundcore Aeroclip, as they provide excellent microphone clarity without breaking the bank. </p><h2 class="article-body__section" id="section-soundpeats-uu2-review-battery-life"><span>SoundPEATS UU2 review: Battery life</span></h2><p>The SoundPEATS UU2 earbuds have very impressive battery life. They have a total playtime of 42 hours, which is two hours more than the EarFun Clip2. Again, I can’t believe the SoundPEATS buds are so cheap! This puts more expensive models, like the Beyerdynamic ZEROs, to shame, as they only have up to 20 hours of battery life!</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="P49Tp7VXNtzGjh9tiVvFEF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/P49Tp7VXNtzGjh9tiVvFEF.jpg" 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 UU2s take an hour and 36 minutes to charge up, and the buds take 45 minutes to charge in the case. You can use the included USB-C cable to charge the case.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-verdict"><span>SoundPEATS UU2 review: Verdict </span></h2><p>After testing using the SoundPEATS UU2 earbuds for two weeks, I’m still so shocked by the sub-$50 price tag. They are seriously excellent value for money. The sound performance blew me away, and apart from some slight bass distortion at higher volumes, they performed very well across a variety of genres. They’re comfortable to wear all day long, have responsive button controls and 42 hours of battery life with the charging case.</p><p>They have a few issues, but these are fairly minor — not to mention forgivable for $49. The call quality isn’t the best, and there’s some slight movement of the buds during high-intensity workouts. The app is also clunky to use. With that said, I really don’t think you can go wrong with these earbuds at this price point. They’re a solid addition to anyone’s audio gear rotation.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/soundpeats-uu2-review</link>
                                                                            <description>
                            <![CDATA[ The SoundPEATS UU2 earbuds are a budget dream and prove that you can get great audio for less, even with some minor issues. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ZZ2N3B4W4cg9LJkxpnKcXe</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/xPXMoHH9y6yzuSWmHL5DEF-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 06 Sep 2026 12:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Earbuds]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/xPXMoHH9y6yzuSWmHL5DEF-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[SoundPEATS UU2 photographed in front of a blue background]]></media:description>                                                            <media:text><![CDATA[SoundPEATS UU2 photographed in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[SoundPEATS UU2 photographed in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/xPXMoHH9y6yzuSWmHL5DEF-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I’m a firm believer that everyone needs a pair of open-ear earbuds in their audio gear rotation, and if you’re still a skeptic, the SoundPEATS UU2 earbuds are the perfect affordable way to give ambient sound with your music a try. </p><p>These earbuds are excellent value for money — at less than $50, they also boast amazing sound performance that I’m more than happy to listen to all day long. They’re comfortable to wear and have button controls which are very responsive. Not to mention the 42 hours of battery life, which is some of the best on the market. </p><p>Unfortunately, there are some things that SoundPEATS could improve on. The UU2’s call quality isn’t the best; there’s some slight movement when wearing the earbuds for high-intensity workouts; and the companion app is clunky. If you’d like to find out more about the buds, you can keep reading this full SoundPEATS UU2 review.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-specs"><span>SoundPEATS UU2 review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://soundpeats.com/en-gb/products/uu2-clip-on-earbuds" target="_blank" rel="nofollow">$49</a> / <a href="https://www.amazon.co.uk/SoundPEATS-UU2-Open-Ear-Earbuds-Physical-Black/dp/B0H4JJRKVY" target="_blank" rel="nofollow">$59</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>1.68 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Buds only weight</strong></p></td><td  ><p>0.18 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Controls</strong></p></td><td  ><p>Buttons</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IPX5</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 6.0</p></td></tr><tr><td class="firstcol " ><p><strong>Supported codecs</strong></p></td><td  ><p>SBC/AAC/LDAC</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>42 hours</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-soundpeats-uu2-review-price-availability"><span>SoundPEATS UU2 review: Price & availability</span></h2><p>The first thing to note is that these headphones have undergone a name change. They were previously named the POP Clip2, but now go by SoundPEATS UU2. The earbuds are available to purchase in the U.S. directly from the <a href="https://soundpeats.com/en-gb/products/uu2-clip-on-earbuds" target="_blank" rel="nofollow">SoundPEATS website for $49</a> and in the U.K. on <a href="https://www.amazon.co.uk/SoundPEATS-UU2-Open-Ear-Earbuds-Physical-Black/dp/B0H4JJRKVY" target="_blank" rel="nofollow">Amazon for £59</a>. </p><p>These are the cheapest open-ear earbuds I’ve tested, but, in all honesty, when I listened to my music with them before knowing the price, I thought they would cost well over $100. But if it’s affordable audio gear you’re looking for, there are a couple of options depending on the fit you want. </p><p>The UU2 are clip-style earbuds, like the <a href="https://www.tomsguide.com/audio/earbuds/earfun-clip-2-review">EarFun Clip 2</a> for $99. I find this the most comfortable style, especially for working out and all-day wear. And the Clip 2 earbuds have features like live translation and impressive call quality, which you don’t get with the UU2s.  You can also get the original <a href="https://www.tomsguide.com/audio/earbuds/earfun-clip-review">EarFun Clip</a> earbuds, which have a great bass performance for $69. This will be better if you like listening to more bass-driven music. </p><p>Alternatively, you could look at a hook design. My favorite budget hook buds are the <a href="https://www.tomsguide.com/audio/earbuds/jlab-epic-open-sport-review">JLab Epic Open Sport</a> for $114. They work best for high-intensity workouts like running, so if you need something that stays secure, these could be the buds for you. However, the sound quality is a mixed bag, and the UU2s offer a more consistent sound.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-design"><span>SoundPEATS UU2 review: Design </span></h2><p>The SoundPEATS UU2 open-earbuds feel premium despite their sub-$50 price tag. They look very similar to the EarFun Clip 2, but have a shinier finish, which I think makes them feel a little more premium. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="G2qurW8saCDG4yGpAmpeDF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/G2qurW8saCDG4yGpAmpeDF.jpg" 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 case is small and fits in the palm of my hand, so it’s easy to slip into my pocket when I’m using the buds. They have an IPX5 durability rating, which means they’re protected from water splashes but not dust. This makes them ideal for walking outdoors in case you get caught out in the rain, but not for jogging on the beach in case you drop them in the sand. This kind of rating is standard for earbuds, but I would like to see some dust protection, as you get with the JLab Epic Open Sport.</p><h3 id="comfort-2">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qj5RokMc4NpVRZLnA8spAF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/qj5RokMc4NpVRZLnA8spAF.jpg" 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>SoundPEATS say that the two Us in the name stand for ‘Ultra Comfort and Ultimate Freedom’; I’d have to agree. I could wear them all day long without them hurting since the buds are so lightweight at 0.18 ounces each. </p><p>When wearing the buds during lighter movement, such as yoga and Pilates, they felt very secure, but there is some slight movement during high-intensity workouts like running or HIIT, so keep this in mind. I found the <a href="https://www.tomsguide.com/audio/earbuds/soundcore-aeroclip-review">Soundcore AeroClip</a> earbuds to be more secure, but they are more expensive at $129.</p><h3 id="controls-3">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="m7urYWDk2YNCP43L5AdxDF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/m7urYWDk2YNCP43L5AdxDF.jpg" 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 SoundPEATS UU2 are controlled using physical buttons. These were responsive and easy to operate, and you can even customize this in the SoundPEATS app, which I’ll talk more about later. I much prefer these controls to touch-sensitive controls, as when you're moving, there’s less chance of a missed command, as it takes more intent to click a button. </p><h2 id="connectivity-3">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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Lxcu99dXLxdPUk5V7acHEF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/Lxcu99dXLxdPUk5V7acHEF.jpg" 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 SoundPEATS UU2 earbuds use the latest (at the time of writing) Bluetooth 6.0, which delivers better power consumption and connection stability. Codec-wise, they support SBC, AAC and LDAC audio codecs, the latter allowing hi-res wireless playback. They also support multipoint connection with up to two devices, which is ideal for when I’m working and need to switch between my phone and laptop for calls. It’s easy to connect multiple devices by using the SoundPEATS app.</p><h3 class="article-body__section" id="section-soundpeats-uu2-review-sound-quality"><span>SoundPEATS UU2 review: Sound quality</span></h3><p>The SoundPEATS UU2 earbuds don’t provide ultimate high-end sound quality — we’re not talking <a href="https://www.tomsguide.com/audio/earbuds/beyerdynamic-amiron-zero-review">Beyerdynamic AMIRON Zero</a> ($179) performance here — but for the price tag, I really can’t ask for any more. They’ve blown me away. I tried out the EQ adjustment settings in the app, but ended up sticking with the default tuning as it was the best for me. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="GydusHNMSMNdkev5WtGzCF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/GydusHNMSMNdkev5WtGzCF.jpg" 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>Firstly, I wanted to listen to the bass performance of the buds, because open-earbuds can often struggle with bass replication since the drivers are outside of your ear. However, I was impressed. I listened to <em>Heads Will Roll </em>by the Yeah Yeah Yeahs, the A-Trak remix, which relies on bass for a heavy club sound, with the vocals adding more structure. The bass was mostly clear, with some very slight distortion when increasing the volume. The synth also sounded great — exactly what I want from this song to feel motivated while working out. </p><p>Next, I listened to <em>Favourite</em> by Fontaines D.C. to test out the mid-range, because of its catchy guitar track and prominent drums. The track was bright and well balanced. I could hear the guitar in detail, and it was punchy and strong. The guitar and drums weren’t overpowering though, and the vocals still shone 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TFzYvYveEUGPzqGv52Pc9F" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/TFzYvYveEUGPzqGv52Pc9F.jpg" 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 the high-frequency performance, I listened to <em>My Tears Ricochet</em> by Taylor Swift. This song uses orchestral elements such as violin and choral harmonies. All of these elements were clear and balanced, leaving space for soft vocals to shine before the song builds into something more powerful. And even then, the earbuds held their own impressively.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-app"><span>SoundPEATS UU2 review: App</span></h2><p>I usually love using an app with earbuds as it adds a layer of customization that makes the listening experience times better. However, I found the SoundPEATS app clunky, especially during the initial setup. With use, it became easier to navigate, but I much prefer apps like the one from Soundcore that are instantly usable. </p><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="3paWsmwN8cHyV9NAeQ58ud" name="soundpeats app" alt="screenshot taken from the SoundPEATS app for the SoundPEATS UU2 earbuds showing the settings" src="https://cdn.mos.cms.futurecdn.net/3paWsmwN8cHyV9NAeQ58ud.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: SoundPEATS)</span></figcaption></figure><p>That said, the app has some useful features. You can enable Spatial audio or make EQ and control adjustments. There’s also a drainage function, which helps expel moisture from inside the earbuds by playing low-frequency tones, effectively shaking out the water. I’m not sure how much this will actually help if you drop your earbuds into a pool for argument's sake, but it’s an interesting feature nonetheless.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-call-quality"><span>SoundPEATS UU2 review: Call quality</span></h2><p>To evaluate the call quality of the SoundPEATS UU2 earbuds, I carried out my usual test and called my mom. She let me know that I didn’t sound as clear as usual. This was disappointing, as these buds are very impressive in pretty much every other area. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="m7urYWDk2YNCP43L5AdxDF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/m7urYWDk2YNCP43L5AdxDF.jpg" 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 pair of open buds for work calls that are still budget-friendly, I’d recommend the Soundcore Aeroclip, as they provide excellent microphone clarity without breaking the bank. </p><h2 class="article-body__section" id="section-soundpeats-uu2-review-battery-life"><span>SoundPEATS UU2 review: Battery life</span></h2><p>The SoundPEATS UU2 earbuds have very impressive battery life. They have a total playtime of 42 hours, which is two hours more than the EarFun Clip2. Again, I can’t believe the SoundPEATS buds are so cheap! This puts more expensive models, like the Beyerdynamic ZEROs, to shame, as they only have up to 20 hours of battery life!</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="P49Tp7VXNtzGjh9tiVvFEF" name="SoundPEATS UU2" alt="SoundPEATS UU2 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/P49Tp7VXNtzGjh9tiVvFEF.jpg" 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 UU2s take an hour and 36 minutes to charge up, and the buds take 45 minutes to charge in the case. You can use the included USB-C cable to charge the case.</p><h2 class="article-body__section" id="section-soundpeats-uu2-review-verdict"><span>SoundPEATS UU2 review: Verdict </span></h2><p>After testing using the SoundPEATS UU2 earbuds for two weeks, I’m still so shocked by the sub-$50 price tag. They are seriously excellent value for money. The sound performance blew me away, and apart from some slight bass distortion at higher volumes, they performed very well across a variety of genres. They’re comfortable to wear all day long, have responsive button controls and 42 hours of battery life with the charging case.</p><p>They have a few issues, but these are fairly minor — not to mention forgivable for $49. The call quality isn’t the best, and there’s some slight movement of the buds during high-intensity workouts. The app is also clunky to use. With that said, I really don’t think you can go wrong with these earbuds at this price point. They’re a solid addition to anyone’s audio gear rotation.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tried a music hammock that vibrates to the beat of any song — and I wanted to take it home with me ]]></title>
                                                                                                <dc:content><![CDATA[ <p>If I could buy one thing from <a href="https://www.tomsguide.com/tag/ifa-2026">IFA 2026</a>, it would be the "Vibrating Music Hammock". Unfortunately for me (and you), you can't buy it. The <a href="https://www.parkerhertzberger.nl/portfolio-collections/my-portfolio/project-title-6" target="_blank" rel="nofollow">Vibrating Music Hammock is designed by Parker Hertzberger</a>, a designer from the Netherlands, and is more of a festival installation than a purchasable experience. </p><p>The Vibrating Music Hammock is oh-so fun. Effectively, it's a hammock with a steel frame that vibrates. The vibrations follow the bass and rhythm of any song (as long as it's bassy, of course), resulting in a completely immersive, cocoon-like experience. And yes, I would install this in my yard in a heartbeat if I could buy it. </p><p>IFA 2026 is full of innovative, weird, and wacky tech, from a <a href="https://www.tiktok.com/@tomsguide/video/7681452295858539790?lang=en" target="_blank" rel="nofollow">drone + action camera combo</a> to a <a href="https://www.tiktok.com/@tomsguide/video/7681806710968782111?lang=en" target="_blank" rel="nofollow">vacuum cleaner for your vacuum cleaner</a>. I even tried an AI-assisted espresso machine! Even though our <a href="https://www.tomsguide.com/tech-events/best-of-ifa-2026-we-found-the-26-top-new-gadgets">Best of IFA awards</a> didn't include a music hammock section, this is the music hammock of my heart.</p><h2 id="so-how-does-the-vibrating-music-hammock-work">So how does the vibrating music hammock work?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dUXr7JtyHWdZRwhsMLk2bF" name="music hammock 2" alt="photos of the vibrating music hammock sensory instillation" src="https://cdn.mos.cms.futurecdn.net/dUXr7JtyHWdZRwhsMLk2bF.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>If you've ever been to a live concert, you'll know that you can <em>feel</em> loud bass. This is because low sound frequencies have more physical energy and carry more air. In essence, the vibrating music hammock commands these sound waves into a physical sensation. Imagine Thor wielding thunder — that's the music hammock with low-frequency sound waves. </p><p>Thanks to the steel frames, the sound waves travel through the hammock's fabric, and through your body.</p><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="B3q2YBqiNxvWZ4nV5nmbKF" name="music hammock 3" alt="photos of the vibrating music hammock sensory instillation" src="https://cdn.mos.cms.futurecdn.net/B3q2YBqiNxvWZ4nV5nmbKF.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As it's a sensory instillation, the vibrating music hammock is intended to immerse you in the music rather than behave like a traditional speaker. There's not much detail in the music, so don't expect one of the best speakers attached here — it's all about the vibes (literally). </p><p>My entire body vibrated to the rhythm of the electronic music on offer, and I honestly could've stayed there all day. I could've gone straight to sleep. My back no longer hurt. My feet stopped aching. This was utterly relaxing and immersive, like the music cousin of a sensory deprivation tank. </p><h2 id="so-why-can-39-t-i-just-buy-one">So why can't I just buy one?</h2><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="oyT7ThjBV3sab3j5bZJdhF" name="music hammock 5" alt="photos of the vibrating music hammock sensory instillation" src="https://cdn.mos.cms.futurecdn.net/oyT7ThjBV3sab3j5bZJdhF.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As I mentioned earlier, this isn't a product you can simply buy and own. It's a festival or event experience. The designer, Parker Hertzberger, manually delivers, sets up, and mans the instillation throughout the event. I'm sure he's got better things to do than come to my backyard and build me a few hammocks.</p><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="kG2XQ8YXzjJVKVuqmmb6QF" name="music hammock 4" alt="photos of the vibrating music hammock sensory instillation" src="https://cdn.mos.cms.futurecdn.net/kG2XQ8YXzjJVKVuqmmb6QF.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Even so, I can't help but wonder if making my own vibrating music hammock might be easier than it sounds. It's fabric and steel, right? Famous last words. </p><p>I'll leave making vibrating music hammocks to the experts, but a girl can dream.</p><p>Would you like a music hammock in your backyard? Feel free to let me know in the comments. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="g1WBrp" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><strong></strong><a href="https://www.tomsguide.com/computing/laptops/dell-14s-hands-on-ifa-2026"><strong>I used Dell's latest MacBook Neo rival — and I'm not complaining about having another one</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/home/home-appliances/i-just-watched-a-robot-vacuum-actually-climb-stairs-it-took-3-minutes-but-those-steps-were-sparkling-after"><strong>I just watched a robot vacuum actually climb stairs — it took 3 minutes but those steps were sparkling after</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/home/irobots-new-flagship-roomba-creates-an-airtight-seal-with-your-carpet-the-competition-should-be-worried"><strong>iRobot's new flagship Roomba creates an airtight seal with your carpet — the competition should be worried</strong></a><strong></strong></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/i-tried-a-music-hammock-that-vibrates-to-the-beat-of-any-song-and-i-wanted-to-take-it-home-with-me</link>
                                                                            <description>
                            <![CDATA[ I tried a music hammock that vibrates to the beat of any song — and I wanted to take it home with me ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">kSmDRKpxAnZgptnbm3Aa6J</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/cQu5CB7bCwhEmzUcFUuSGb-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 05 Sep 2026 13:53:59 +0000</pubDate>                                                                                                                                <updated>Sat, 05 Sep 2026 13:54:30 +0000</updated>
                                                                                                                                            <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.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/cQu5CB7bCwhEmzUcFUuSGb-1280-80.jpg">
                                                            <media:credit><![CDATA[Erin Bashford]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[a photo of the author in a lime green music hammock]]></media:description>                                                            <media:text><![CDATA[a photo of the author in a lime green music hammock]]></media:text>
                                <media:title type="plain"><![CDATA[a photo of the author in a lime green music hammock]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/cQu5CB7bCwhEmzUcFUuSGb-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If I could buy one thing from <a href="https://www.tomsguide.com/tag/ifa-2026">IFA 2026</a>, it would be the "Vibrating Music Hammock". Unfortunately for me (and you), you can't buy it. The <a href="https://www.parkerhertzberger.nl/portfolio-collections/my-portfolio/project-title-6" target="_blank" rel="nofollow">Vibrating Music Hammock is designed by Parker Hertzberger</a>, a designer from the Netherlands, and is more of a festival installation than a purchasable experience. </p><p>The Vibrating Music Hammock is oh-so fun. Effectively, it's a hammock with a steel frame that vibrates. The vibrations follow the bass and rhythm of any song (as long as it's bassy, of course), resulting in a completely immersive, cocoon-like experience. And yes, I would install this in my yard in a heartbeat if I could buy it. </p><p>IFA 2026 is full of innovative, weird, and wacky tech, from a <a href="https://www.tiktok.com/@tomsguide/video/7681452295858539790?lang=en" target="_blank" rel="nofollow">drone + action camera combo</a> to a <a href="https://www.tiktok.com/@tomsguide/video/7681806710968782111?lang=en" target="_blank" rel="nofollow">vacuum cleaner for your vacuum cleaner</a>. I even tried an AI-assisted espresso machine! Even though our <a href="https://www.tomsguide.com/tech-events/best-of-ifa-2026-we-found-the-26-top-new-gadgets">Best of IFA awards</a> didn't include a music hammock section, this is the music hammock of my heart.</p><h2 id="so-how-does-the-vibrating-music-hammock-work">So how does the vibrating music hammock work?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dUXr7JtyHWdZRwhsMLk2bF" name="music hammock 2" alt="photos of the vibrating music hammock sensory instillation" src="https://cdn.mos.cms.futurecdn.net/dUXr7JtyHWdZRwhsMLk2bF.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>If you've ever been to a live concert, you'll know that you can <em>feel</em> loud bass. This is because low sound frequencies have more physical energy and carry more air. In essence, the vibrating music hammock commands these sound waves into a physical sensation. Imagine Thor wielding thunder — that's the music hammock with low-frequency sound waves. </p><p>Thanks to the steel frames, the sound waves travel through the hammock's fabric, and through your body.</p><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="B3q2YBqiNxvWZ4nV5nmbKF" name="music hammock 3" alt="photos of the vibrating music hammock sensory instillation" src="https://cdn.mos.cms.futurecdn.net/B3q2YBqiNxvWZ4nV5nmbKF.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As it's a sensory instillation, the vibrating music hammock is intended to immerse you in the music rather than behave like a traditional speaker. There's not much detail in the music, so don't expect one of the best speakers attached here — it's all about the vibes (literally). </p><p>My entire body vibrated to the rhythm of the electronic music on offer, and I honestly could've stayed there all day. I could've gone straight to sleep. My back no longer hurt. My feet stopped aching. This was utterly relaxing and immersive, like the music cousin of a sensory deprivation tank. </p><h2 id="so-why-can-39-t-i-just-buy-one">So why can't I just buy one?</h2><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="oyT7ThjBV3sab3j5bZJdhF" name="music hammock 5" alt="photos of the vibrating music hammock sensory instillation" src="https://cdn.mos.cms.futurecdn.net/oyT7ThjBV3sab3j5bZJdhF.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>As I mentioned earlier, this isn't a product you can simply buy and own. It's a festival or event experience. The designer, Parker Hertzberger, manually delivers, sets up, and mans the instillation throughout the event. I'm sure he's got better things to do than come to my backyard and build me a few hammocks.</p><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="kG2XQ8YXzjJVKVuqmmb6QF" name="music hammock 4" alt="photos of the vibrating music hammock sensory instillation" src="https://cdn.mos.cms.futurecdn.net/kG2XQ8YXzjJVKVuqmmb6QF.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Even so, I can't help but wonder if making my own vibrating music hammock might be easier than it sounds. It's fabric and steel, right? Famous last words. </p><p>I'll leave making vibrating music hammocks to the experts, but a girl can dream.</p><p>Would you like a music hammock in your backyard? Feel free to let me know in the comments. </p><figure class="inline-layout"><div style="min-height: 400px;"><fw-embed-feed channel="toms_guide" playlist="g1WBrp" mode="row" player_placement="bottom-right"></fw-embed-feed></div></figure><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><strong></strong><a href="https://www.tomsguide.com/computing/laptops/dell-14s-hands-on-ifa-2026"><strong>I used Dell's latest MacBook Neo rival — and I'm not complaining about having another one</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/home/home-appliances/i-just-watched-a-robot-vacuum-actually-climb-stairs-it-took-3-minutes-but-those-steps-were-sparkling-after"><strong>I just watched a robot vacuum actually climb stairs — it took 3 minutes but those steps were sparkling after</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/home/irobots-new-flagship-roomba-creates-an-airtight-seal-with-your-carpet-the-competition-should-be-worried"><strong>iRobot's new flagship Roomba creates an airtight seal with your carpet — the competition should be worried</strong></a><strong></strong></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ 'We've learned a ton about what customers actually want': Sonos execs on earning back trust with Sonos 27 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As a Formula 1 engine ripped through the center channel of the new <a href="https://www.tomsguide.com/audio/soundbars/sonos-is-ready-to-steal-back-the-soundbar-crown-with-the-new-beam-ultra">Sonos Beam Ultra</a>, I decided I simply was not audibly immersed enough. I picked up two <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> speakers from a side shelf and set them down on a long table behind the couch. </p><p>Without touching a phone or digging through a settings menu, the Sonos system registered a subtle, satisfying "boop." The accelerometers inside the portables recognized they’d been placed down, fired off an imperceptible ultrasonic chirp to locate the soundbar and instantly reconfigured themselves into active rear surround channels.</p><p>But remembering I was in a public setting and should <em>probably</em> respect everyone else’s ears, I double-tapped the physical action button on the earcup of the new <a href="https://www.tomsguide.com/audio/headphones/i-tried-sonos-ace-ultra-and-it-does-something-your-bluetooth-headphones-cant">Ace Ultra headphones</a>. The roar of the race track instantly lifted off the soundbar and dropped straight into my ears. </p><p>No Bluetooth pairing screen, no app lag — just an instantaneous network handoff.</p><p>After <a href="https://www.tomsguide.com/audio/the-sonos-dilemma-what-does-the-future-hold-for-the-smart-speaker-firm" target="_blank">two years of software headaches,</a> this seamless handoff felt like the turning point Sonos has so desperately needed.</p><h2 id="earning-back-trust-with-sonos-27">Earning back trust with Sonos 27</h2><p>It’s no secret that Sonos software has faced <a href="https://www.tomsguide.com/audio/sonos-walks-back-some-of-its-controversial-app-changes-adds-back-a-sleep-timer">intense scrutiny</a> from its most passionate users, primarily via Reddit, over the last two years or so. </p><p>But rather than dancing around the past, Sonos is framing its <a href="https://www.tomsguide.com/audio/speakers/sonos-27-is-here-to-clean-up-the-app-mess-again-this-time-with-ai">new operating system, Sonos 27</a>, as a direct answer to that feedback. In fact, surveying the room, I learned that members of the Sonos Reddit community were actively participating in demos right alongside the media.</p><p>"We’ve been listening to customers all along, but especially in the last two years... we've learned a ton about what customers actually want," explained Andrew Sutherland, Sonos Senior Director of Product Management during an interview with me following the event. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="23iLGcWhKptGvSXmHxMt8R" name="Sonos-27screens_16x9_3" alt="Sonos 27 app" src="https://cdn.mos.cms.futurecdn.net/23iLGcWhKptGvSXmHxMt8R.png" 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: Sonos)</span></figcaption></figure><p>"Internally, the way we're thinking about testing and releasing things has really been bolstered,” said Sutherland. “A really good example is the app redesign with tabs in the last three months, which has actually been behind a test that users can opt into and try out."</p><p>Beyond core stability and opt-in testing, Sonos 27 introduces open platform AI control via Model Context Protocol (MCP), letting users connect outside assistants like ChatGPT alongside Sonos' own voice engine or custom agents. </p><p>"It always comes back to choice and being an open platform for control," Sutherland said. "We don't really have skin in the game to say one intelligence should be used over the other."</p><h2 id="ecosystem-first-hardware-design">Ecosystem-first hardware design</h2><p>While competitors like Sony and Bose dominate commuter noise cancellation, Sonos is carving out a hybrid category with the $449 Ace Ultra: high-end cans designed to act as an instant extension of your living room system.</p><p>"These headphones are definitely the best headphones you can use at home, no doubt. But they're also the best headphones you can use away from the home," Brandon Holley, Sonos Interim Hardware Lead, clarified to me when I asked about Ace Ultra’s competition.</p><p>"We worked incredibly hard to make them light, but also extremely comfortable. So when you walk in the front door, it's an amazing experience that you don't need to leave at the front door."</p><div><blockquote><p>"When you walk in the front door, it's an amazing experience that you don't need to leave at the front door."</p><p>Brandon Holley, Sonos Interim Hardware Lead</p></blockquote></div><p>Under the hood, bringing Wi-Fi system-linking directly into a wearable via Headphone Engine 2 required a complete internal redesign. </p><p>"You have to get into the radio. That means making sure you have space for the antennas, the radio, and moving electronics so the thermal design works," Holley said. </p><p>Acoustically, the Ace Ultra upgrades its predecessor's hardware for a custom-engineered 40mm driver optimized for sheer dynamic range.</p><div class="product"><a data-dimension112="618c8d6e-a875-11f1-bfce-2139999b8357" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449" href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:536px;"><p class="vanilla-image-block" style="padding-top:108.58%;"><img id="jEBPbWV6VERCE8peQaX9Xb" name="Ace Ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jEBPbWV6VERCE8peQaX9Xb.png" mos="" align="middle" fullscreen="" width="536" height="582" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow" data-dimension112="618c8d6e-a875-11f1-bfce-2139999b8357" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449">Ace Ultra: $449</a></strong><br>Noise-canceling headphones with rich, detailed audio, up to 35 hours of battery with ANC on, and a direct connection to your Sonos system.<a class="view-deal button" href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow" data-dimension112="618c8d6e-a875-11f1-bfce-2139999b8357" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449">View Deal</a></p></div><p>"When you're creating sound, you're trying to transfer mechanical energy into movement of air. Power-to-weight ratio—if you want to think about it in terms of automobiles—that is king," Holley emphasized. "You want as much power as you can with as light of a diaphragm as you can make."</p><p>Meanwhile, on the Beam Ultra front, Holley revealed that fitting nine custom drivers (including two dedicated up-firing speakers) into the mid-size Beam Ultra meant engineering every component completely from scratch to solve cabinet displacement constraints.</p><h2 id="the-unseen-magic-of-sonos-fabric">The unseen magic of Sonos Fabric</h2><p>The thread binding this new hardware generation together is Sonos Fabric, the underlying software capability that lets devices sense each other in physical space. Whether it's enabling portable speakers to act as instant, dynamic rear surrounds or letting headphones pull sound straight off a soundbar, Sonos’s goal is to eliminate manual configuration entirely.</p><p>"Fabric is the set of technologies that allows us to be able to do that," said Sutherland. "You shouldn't have to go into an app to set those things up or change them. It should just feel natural in your home. You almost don't want anyone to actually talk about Fabric, you just want it to happen."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:400px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xYQZkazuv9yyhnRUum5NDb" name="Headphone linking" alt="Sonos ecosystem animated" src="https://cdn.mos.cms.futurecdn.net/xYQZkazuv9yyhnRUum5NDb.gif" mos="" align="middle" fullscreen="" width="400" height="225" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sonos)</span></figcaption></figure><p>When the roar of an F1 engine can jump from a soundbar, route through two self-configuring rear surrounds, and land straight in your headphones with a single tap (all without looking at a phone screen) I’m able to recognize what Sonos is actually fighting for at this moment in the company’s 20-plus-year history.</p><p>For loyalists who felt burned by Sonos' recent software missteps, Sonos 27 and this new hardware push send a clear message: Sonos is dedicated to making its ecosystem feel effortless again.</p><p>When the sound follows you from the soundbar to your headset without a single glance at your phone, you don't care how complex the underlying tech is. You're just glad it finally gets out of your way.</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/soundbars/sonos-is-ready-to-steal-back-the-soundbar-crown-with-the-new-beam-ultra">Sonos is ready to steal back the soundbar crown with the new Beam Ultra</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/i-just-set-up-the-ultimate-sonos-surround-system-despite-some-connection-foibles-its-here-to-stay">I just set up the ultimate Sonos surround system</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/change-these-sonos-soundbar-settings-immediately-to-get-the-best-performance-out-of-your-tv-and-movie-watching">Change these Sonos soundbar settings immediately</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/weve-learned-a-ton-about-what-customers-actually-want-sonos-execs-on-earning-back-trust-with-sonos-27</link>
                                                                            <description>
                            <![CDATA[ In an interview with Sonos leadership, we explore how Sonos 27 and the new Ultra hardware aim to win back loyalists by making whole-home audio handoff effortless again. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">oukEL6YDyB4KK5zzLFeGF5</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/CwisPuyqPq9rHXbWpk2PfH-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 05 Sep 2026 09:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Kate Kozuch ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xAVUdx6Qtp3SzugnnfNYsL.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/CwisPuyqPq9rHXbWpk2PfH-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sonos Ace Ultra connected to Sonos Beam Ultra soundbar underneath TV]]></media:description>                                                            <media:text><![CDATA[Sonos Ace Ultra connected to Sonos Beam Ultra soundbar underneath TV]]></media:text>
                                <media:title type="plain"><![CDATA[Sonos Ace Ultra connected to Sonos Beam Ultra soundbar underneath TV]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/CwisPuyqPq9rHXbWpk2PfH-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As a Formula 1 engine ripped through the center channel of the new <a href="https://www.tomsguide.com/audio/soundbars/sonos-is-ready-to-steal-back-the-soundbar-crown-with-the-new-beam-ultra">Sonos Beam Ultra</a>, I decided I simply was not audibly immersed enough. I picked up two <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> speakers from a side shelf and set them down on a long table behind the couch. </p><p>Without touching a phone or digging through a settings menu, the Sonos system registered a subtle, satisfying "boop." The accelerometers inside the portables recognized they’d been placed down, fired off an imperceptible ultrasonic chirp to locate the soundbar and instantly reconfigured themselves into active rear surround channels.</p><p>But remembering I was in a public setting and should <em>probably</em> respect everyone else’s ears, I double-tapped the physical action button on the earcup of the new <a href="https://www.tomsguide.com/audio/headphones/i-tried-sonos-ace-ultra-and-it-does-something-your-bluetooth-headphones-cant">Ace Ultra headphones</a>. The roar of the race track instantly lifted off the soundbar and dropped straight into my ears. </p><p>No Bluetooth pairing screen, no app lag — just an instantaneous network handoff.</p><p>After <a href="https://www.tomsguide.com/audio/the-sonos-dilemma-what-does-the-future-hold-for-the-smart-speaker-firm" target="_blank">two years of software headaches,</a> this seamless handoff felt like the turning point Sonos has so desperately needed.</p><h2 id="earning-back-trust-with-sonos-27">Earning back trust with Sonos 27</h2><p>It’s no secret that Sonos software has faced <a href="https://www.tomsguide.com/audio/sonos-walks-back-some-of-its-controversial-app-changes-adds-back-a-sleep-timer">intense scrutiny</a> from its most passionate users, primarily via Reddit, over the last two years or so. </p><p>But rather than dancing around the past, Sonos is framing its <a href="https://www.tomsguide.com/audio/speakers/sonos-27-is-here-to-clean-up-the-app-mess-again-this-time-with-ai">new operating system, Sonos 27</a>, as a direct answer to that feedback. In fact, surveying the room, I learned that members of the Sonos Reddit community were actively participating in demos right alongside the media.</p><p>"We’ve been listening to customers all along, but especially in the last two years... we've learned a ton about what customers actually want," explained Andrew Sutherland, Sonos Senior Director of Product Management during an interview with me following the event. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="23iLGcWhKptGvSXmHxMt8R" name="Sonos-27screens_16x9_3" alt="Sonos 27 app" src="https://cdn.mos.cms.futurecdn.net/23iLGcWhKptGvSXmHxMt8R.png" 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: Sonos)</span></figcaption></figure><p>"Internally, the way we're thinking about testing and releasing things has really been bolstered,” said Sutherland. “A really good example is the app redesign with tabs in the last three months, which has actually been behind a test that users can opt into and try out."</p><p>Beyond core stability and opt-in testing, Sonos 27 introduces open platform AI control via Model Context Protocol (MCP), letting users connect outside assistants like ChatGPT alongside Sonos' own voice engine or custom agents. </p><p>"It always comes back to choice and being an open platform for control," Sutherland said. "We don't really have skin in the game to say one intelligence should be used over the other."</p><h2 id="ecosystem-first-hardware-design">Ecosystem-first hardware design</h2><p>While competitors like Sony and Bose dominate commuter noise cancellation, Sonos is carving out a hybrid category with the $449 Ace Ultra: high-end cans designed to act as an instant extension of your living room system.</p><p>"These headphones are definitely the best headphones you can use at home, no doubt. But they're also the best headphones you can use away from the home," Brandon Holley, Sonos Interim Hardware Lead, clarified to me when I asked about Ace Ultra’s competition.</p><p>"We worked incredibly hard to make them light, but also extremely comfortable. So when you walk in the front door, it's an amazing experience that you don't need to leave at the front door."</p><div><blockquote><p>"When you walk in the front door, it's an amazing experience that you don't need to leave at the front door."</p><p>Brandon Holley, Sonos Interim Hardware Lead</p></blockquote></div><p>Under the hood, bringing Wi-Fi system-linking directly into a wearable via Headphone Engine 2 required a complete internal redesign. </p><p>"You have to get into the radio. That means making sure you have space for the antennas, the radio, and moving electronics so the thermal design works," Holley said. </p><p>Acoustically, the Ace Ultra upgrades its predecessor's hardware for a custom-engineered 40mm driver optimized for sheer dynamic range.</p><div class="product"><a data-dimension112="618c8d6e-a875-11f1-bfce-2139999b8357" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449" href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:536px;"><p class="vanilla-image-block" style="padding-top:108.58%;"><img id="jEBPbWV6VERCE8peQaX9Xb" name="Ace Ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jEBPbWV6VERCE8peQaX9Xb.png" mos="" align="middle" fullscreen="" width="536" height="582" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow" data-dimension112="618c8d6e-a875-11f1-bfce-2139999b8357" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449">Ace Ultra: $449</a></strong><br>Noise-canceling headphones with rich, detailed audio, up to 35 hours of battery with ANC on, and a direct connection to your Sonos system.<a class="view-deal button" href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow" data-dimension112="618c8d6e-a875-11f1-bfce-2139999b8357" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449">View Deal</a></p></div><p>"When you're creating sound, you're trying to transfer mechanical energy into movement of air. Power-to-weight ratio—if you want to think about it in terms of automobiles—that is king," Holley emphasized. "You want as much power as you can with as light of a diaphragm as you can make."</p><p>Meanwhile, on the Beam Ultra front, Holley revealed that fitting nine custom drivers (including two dedicated up-firing speakers) into the mid-size Beam Ultra meant engineering every component completely from scratch to solve cabinet displacement constraints.</p><h2 id="the-unseen-magic-of-sonos-fabric">The unseen magic of Sonos Fabric</h2><p>The thread binding this new hardware generation together is Sonos Fabric, the underlying software capability that lets devices sense each other in physical space. Whether it's enabling portable speakers to act as instant, dynamic rear surrounds or letting headphones pull sound straight off a soundbar, Sonos’s goal is to eliminate manual configuration entirely.</p><p>"Fabric is the set of technologies that allows us to be able to do that," said Sutherland. "You shouldn't have to go into an app to set those things up or change them. It should just feel natural in your home. You almost don't want anyone to actually talk about Fabric, you just want it to happen."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:400px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xYQZkazuv9yyhnRUum5NDb" name="Headphone linking" alt="Sonos ecosystem animated" src="https://cdn.mos.cms.futurecdn.net/xYQZkazuv9yyhnRUum5NDb.gif" mos="" align="middle" fullscreen="" width="400" height="225" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sonos)</span></figcaption></figure><p>When the roar of an F1 engine can jump from a soundbar, route through two self-configuring rear surrounds, and land straight in your headphones with a single tap (all without looking at a phone screen) I’m able to recognize what Sonos is actually fighting for at this moment in the company’s 20-plus-year history.</p><p>For loyalists who felt burned by Sonos' recent software missteps, Sonos 27 and this new hardware push send a clear message: Sonos is dedicated to making its ecosystem feel effortless again.</p><p>When the sound follows you from the soundbar to your headset without a single glance at your phone, you don't care how complex the underlying tech is. You're just glad it finally gets out of your way.</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/soundbars/sonos-is-ready-to-steal-back-the-soundbar-crown-with-the-new-beam-ultra">Sonos is ready to steal back the soundbar crown with the new Beam Ultra</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/i-just-set-up-the-ultimate-sonos-surround-system-despite-some-connection-foibles-its-here-to-stay">I just set up the ultimate Sonos surround system</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/change-these-sonos-soundbar-settings-immediately-to-get-the-best-performance-out-of-your-tv-and-movie-watching">Change these Sonos soundbar settings immediately</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Apple Sept. 9 event — iPhone Ultra, iPhone 18 Pro all the new products to expect ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">APPLE EVENT LATEST NEWS AND RUMORS</div><div class="fancy_box_body"><p class="fancy-box__body-text"><em><strong>Updated September 7</strong></em></p><p class="fancy-box__body-text">• This is why the A20 Pro cvhip will be such a big deal for iPhone Ultra and 18 Pro<br>•  Here's what you need to know about the new <a data-analytics-id="inline-link" href="https://www.tomsguide.com/home/smart-home/apple-homepad-rumored-release-date-price-and-all-the-latest-leaks">Apple HomePad</a><br>• The <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/iphone-ultra-price-rumors-heres-how-much-you-can-expect-to-pay-for-apples-first-foldable">iPhone Ultra price</a> could be even higher than you expect<br>• Our i<a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/iphone-ultra-exclusive-gurman-on-price-release-date-and-secret-weapon-versus-android">Phone Ultra preview interview</a> has all the details you need to know<br>• <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/apple-event-is-official-for-september-9-iphone-ultra-iphone-18-pro-max-watch-12-and-more">"Surprise and shine"</a> event officially set for Wednesday, September 9</p></div></div><p>Apple's huge "Surprise and Shine" is just days away and the first major event under the leadership of incoming CEO John Ternus' promises to be a big one. And now we have a fairly clear picture of what the company should announce.</p><p>Bloomberg's Mark Gurman has dropped a <a href="https://www.bloomberg.com/news/newsletters/2026-09-06/apple-s-new-ceo-like-cook-pay-package-shows-he-s-going-nowhere-sept-9-event-mtpvpcj1" target="_blank">massive report</a> that outlines everything Apple should release over the next year. And we expect 9 of those new devices to come at the Apple September event, including Apple's first foldable phone in the <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-rumors-everything-you-need-to-know-about-the-foldable-iphone">iPhone Ultra</a>.</p><p>We've got a full breakdown of the devices likely headed your way, from the <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-rumors-everything-we-know-so-far">iPhone 18 Pr</a>o series and <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-12-and-ultra-4-rumors-your-next-apple-watch-could-have-a-ceramic-case">Apple Watch 12</a> to a new HomePad.</p><h3 class="article-body__section" id="section-when-is-the-apple-event"><span>When is the Apple event?</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2066px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="fnjPCS2zVb2TZ4GCnuqwK5" name="Apple suprise and shine" alt="Apple fall event 2026." src="https://cdn.mos.cms.futurecdn.net/fnjPCS2zVb2TZ4GCnuqwK5.jpg" mos="" align="middle" fullscreen="" width="2066" height="1162" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>Apple has slated its "Surprise and shine" event for <strong>Wednesday, September 9 at Apple Park</strong>. Typically, Apple would hold its event on a Tuesday, but this year it's coming right after Labor Day, meaning it's being pushed to Wednesday.</p><p>This has happened before and Apple's reasoning has been that the company doesn't want to ask attendees to travel on the holiday weekend. As an example, the <a href="https://www.tomsguide.com/reviews/iphone-14">iPhone 14</a> launched on Wednesday, September 7, following Labor Day on Monday 5.</p><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/rQHEN3kngZo" allowfullscreen></iframe></div></div><p>The <strong>event is set to take place at 10am PT / 1pm ET / 6pm BST</strong> and will be livestreamed on various social media platforms as well as Apple's own site.</p><p>iPhones generally go up for pre-order the Friday following the Apple event, but this year that's September 11. Gurman has reported that pre-orders will be <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-iphone-18-pro-pre-order-date-just-tipped-heres-when-you-can-get-yours">pushed to Saturday, September 12</a>.</p><p>New CEO John Ternus is expected to host the monster event, and his tenure appears ready to start with a bang.</p><h2 class="article-body__section" id="section-iphone-18-pro-and-pro-max"><span>iPhone 18 Pro and Pro Max</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:2490px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="bc3v2xma2LGpb9mYSNpejc" name="iphone 18 pro render in dark cherry" alt="iphone 18 pro in dark cherry color from macworld" src="https://cdn.mos.cms.futurecdn.net/bc3v2xma2LGpb9mYSNpejc.png" mos="" align="middle" fullscreen="" width="2490" height="1401" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Macworld)</span></figcaption></figure><p>This year, we believe that Apple will only launch the <a href="https://www.tomsguide.com/phones/iphones/iphone-18-rumors-everything-we-know-so-far">iPhone 18 Pro</a> and <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-max-rumors-and-leaks-everything-we-know-so-far">iPhone 18 Pro Max</a> models. The entry-level iPhone 18 and iPhone 18 Plus reportedly <a href="https://www.tomsguide.com/phones/iphones/iphone-18-release-schedule-could-be-changing-but-this-rumor-makes-no-sense">won't debut until next spring</a> alongside the iPhone 18e.</p><p>The design of the Pro models should look similar to last year's iPhone 17 Pro models. However, the Dynamic Island should be smaller, with a 24MP selfie camera. <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-camera-upgrades-everything-we-know-about-the-new-sensor">Rumors suggest the Pro Max</a> model will also gain exclusive access new <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-could-be-the-first-iphone-to-get-a-variable-aperture-why-that-matters">variable aperture lens</a> and might also have a longer 6x zoom.</p><p>Both models should feature Apple's new 2nm <a href="https://www.tomsguide.com/phones/iphones/apple-a20-pro-chip-why-its-such-a-big-deal-for-the-iphone-ultra-and-iphone-18-pro-max">A20 Pro chip</a> and a new C2 modem. A <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-iphone-18-pro-a20-chip-leak-reveals-the-biggest-gpu-ever-inside-an-iphone">recent leak suggests</a> the A20 Pro could feature the largest ever GPU inside an iPhone, meaning better, more efficient performance. Speaking of performance, the <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-max-battery-rumors-it-could-be-a-record-breaking-year-for-apple">latest rumors</a> suggest the iPhone 18 Pro Max could pack a new battery somewhere between 5,391 mAh and 5,567 mAh, and if Apple pushes the silicon's efficiency performance closer to 30%, it could last over 23 hours on a single charge. </p><p>For more, see our <a href="https://www.tomsguide.com/phones/iphones/3-big-reasons-to-buy-iphone-18-pro-max-over-the-iphone-18-pro-this-year">iPhone 18 Pro vs iPhone 18 Pro Max</a> comparison.</p><p>Unfortunately, <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-could-see-absolutely-massive-price-hike-according-to-researchers">analysts have predicted</a> that the new iPhone 18 Pro models could cost between $200 and $250 more than the iPhone 17 Pro family. Here's hoping you have that extra money saved to cover the difference.</p><h3 class="article-body__section" id="section-iphone-ultra"><span>iPhone Ultra</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="g5Ndi778wD3dSpn6XzB5te" name="iPhone Ultra Back AppleTrack-2" alt="iPhone Ultra render shown in hand" src="https://cdn.mos.cms.futurecdn.net/g5Ndi778wD3dSpn6XzB5te.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: AppleTrack)</span></figcaption></figure><p>The biggest announcement of the Apple event is sure to be the debut of the foldable iPhone Ultra.</p><p>An <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-18-pro-max-unboxed-ahead-of-apple-event-these-could-be-the-final-designs">unboxing video</a> from this week revealed a squat passport shape similar to the new <a href="https://www.tomsguide.com/phones/samsung-phones/samsung-galaxy-z-fold-8-review-wide-for-the-win">Galaxy Z Fold 8</a>, with a <a href="https://www.tomsguide.com/phones/iphones/iphone-fold-specs-potentially-revealed-heres-how-it-would-stack-up-against-samsung-galaxy-z-fold-8">7.8-inch foldable display and a 5.5-inch cover screen</a>. Apple has allegedly made a crease-free display, one of the hangs up that stopped the company from making a foldable until now.</p><p>The Ultra could measure between 4.5 and 4.8mm when unfolded, making it one of the thinnest iPhones ever.</p><p>Inside, the Ultra would also feature the <a href="https://www.tomsguide.com/phones/iphones/iphone-fold-tipped-for-a20-pro-chip-apples-first-2nm-processor-to-power-new-foldable-phone">A20 Pro chipset</a> and Apple's in-house C2 modem, plus a Touch ID fingerprint scanner in the power button — in lieu of Face ID. It's supposed to feature a fairly larger 4,883 mAh battery.</p><p>The rear panel should house two 48MP cameras horizontally positioned (wide and ultrawide) and two separate 24MP selfie cameras — one for the cover display and one for the interior screen. </p><p>Apple reportedly delayed releasing a folding iPhone until it could eliminate the crease in the display, but whether it's accomplished this remains to be seen. Even Samsung and Oppo, despite their long lead time, have only <a href="https://www.tomsguide.com/phones/samsung-phones/comparing-galaxy-z-fold-8-vs-oppo-find-n6-crease">had moderate success</a>.</p><p>One downside is that the <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-price-rumors-heres-how-much-you-can-expect-to-pay-for-apples-first-foldable">iPhone Ultra price</a> could be very expensive, costing anywhere from $2,000 to 2,500. And a recent leak suggests that<a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-leakers-reveal-new-details-around-release-and-extremely-limited-availability"> iPhone Ultra may not be immediately available, </a>with only "extremely limited" availability.</p><p>Just keep in mind that the <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-trade-offs-the-biggest-things-youre-not-getting-versus-iphone-18-pro">iPhone Ultra could have a number of trade-offs</a> versus the iPhone 18 Pro, including the lack of a telephoto zoom.</p><h2 class="article-body__section" id="section-airpods-5"><span>AirPods 5</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="2ZeAyG6YEDw3Uu4QoJaXYA" name="Airpods 4.jpg" alt="Airpods 4" src="https://cdn.mos.cms.futurecdn.net/2ZeAyG6YEDw3Uu4QoJaXYA.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>The rumor mill has been excitedly leaking information about the AirPods Ultra, which could <a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know">feature a camera</a> that will feed into Siri to allow it to "see" the world. But according to a <a href="https://www.tomsguide.com/audio/airpods/gurman-ai-airpods-with-cameras-just-tipped-for-2027-launch-in-flurry-of-new-releases">Mark Gurman report</a>, we aren't expecting the <a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-everything-we-know-so-far-about-apples-camera-earbuds">AirPods Ultra</a> until 2027 at the earliest. </p><p>Instead, we should get a slightly upgraded pair of AirPods 5. AppleTrack claims the design will be similar to the current AirPods 4. </p><p>Only two real upgrades have been suggested, including a better chip, volume control on the buds and a heart sensor similar to the Pro models. From a software side, the new AirPods 5 will likely take advantage of the more <a href="https://www.tomsguide.com/phones/iphones/i-spent-a-week-with-siri-ai-here-are-my-honest-pros-and-cons">powerful Siri AI </a>releasing this fall, though what features it gets remains to be seen.</p><h2 class="article-body__section" id="section-apple-watch-12-and-apple-watch-ultra-4"><span>Apple Watch 12 and Apple Watch Ultra 4</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:589px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="fYpaDpUGy9ie37wUcNrzwZ" name="Apple Watch Ultra 3" alt="Apple Watch Ultra 3" src="https://cdn.mos.cms.futurecdn.net/fYpaDpUGy9ie37wUcNrzwZ.jpg" mos="" align="middle" fullscreen="" width="589" height="331" 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>Similar to the AirPods, the leakers have been fairly quiet about the next Apple Watches. We are expecting to see the <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-series-12-everything-we-know-so-far">Apple Watch Series 12</a> and the <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-ultra-4-these-are-the-upgrades-apple-needs-to-make-to-remain-the-smartwatch-king">Apple Watch Ultra 4</a>.</p><p>The designs shouldn't change too much, though Apple might  <a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">offer a ceramic case </a><a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">option </a>for the new watches. The biggest change might be a <a href="https://www.tomsguide.com/wellness/smartwatches/leaked-apple-watch-12-design-change-is-the-opposite-of-an-upgrade-and-that-has-me-worried">new kind of band attachment</a>. This does mean that your old bands might not be compatible with the new watches.</p><p>AppleTrack suggests Apple will put a new S12 chip in the watches with a focus on speed and battery life, though exactly how fast and how much better was not revealed.</p><p>Both watches should get new health and fitness features. We already know that <a href="https://www.tomsguide.com/wellness/smartwatches/watchos-27-all-the-new-features-coming-to-apple-watch-later-this-year#section-watchos-27-new-health-and-fitness-features">watchOS 27</a> will bring new AI features, an updated Workout Buddy and more useful data tracking. Plus, it should feature better integration with the upgraded Siri AI, which we've already seen in the <a href="https://www.tomsguide.com/phones/iphones/ios-27-public-beta-is-now-live-9-features-you-need-to-try-first">iOS 27 beta</a>.</p><h3 class="article-body__section" id="section-apple-tv-4k"><span>Apple TV 4K</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1280px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="35tUhk2U4oNYnKewrYtsxJ" name="apple tv vpn.jpg" alt="Apple TV" src="https://cdn.mos.cms.futurecdn.net/35tUhk2U4oNYnKewrYtsxJ.jpg" mos="" align="middle" fullscreen="" width="1280" height="720" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Lebedev Yury/Shutterstock)</span></figcaption></figure><p>Apple hasn't released a new version of the <a href="https://www.tomsguide.com/reviews/apple-tv-4k-2022">Apple TV 4K</a> since 2022. We're long overdue for an upgrade — even if it's only a more powerful chip for stronger performance. </p><p>Like everything else this year, don't expect new designs. It should look similar to older editions. Rumors have repeatedly suggested that the streaming box will feature the <a href="https://www.tomsguide.com/entertainment/streaming-devices/new-apple-tv-4k-biggest-upgrades-just-leaked-ahead-of-launch">A17 Pro chip</a> — the same as in the <a href="https://www.tomsguide.com/news/a17-pro">iPhone 15 Pro models</a>. It should also get the in-house N1 chip for better Wi-Fi and Bluetooth connectivity.</p><p>Outside of chips, the big upgrade would be Siri AI, which could let you connect the Apple TV as a hub for smart devices. One rumor suggested that Apple might release a <a href="https://www.bloomberg.com/news/newsletters/2024-04-07/apple-aapl-future-products-could-include-robots-and-smart-home-push-lupinmnahttps:/www.bloomberg.com/news/newsletters/2024-04-07/apple-aapl-future-products-could-include-robots-and-smart-home-push-lupinmna" target="_blank">set-top box with a camera,</a> which would allow for FaceTime calls and gesture-based controls.</p><p>AppleTrack suggests that the new Apple TV will be expensive, possibly costlier than the current edition, which is currently <a href="https://www.tomsguide.com/entertainment/apple-tv/apples-price-hikes-finally-made-me-pick-up-the-apple-tv-4k-ive-been-eyeing-for-weeks-and-you-can-still-grab-one-at-the-old-price">priced at $199 and $179 following price hikes</a> this summer.</p><h2 class="article-body__section" id="section-smart-home"><span>Smart Home</span></h2><p>Since Apple Intelligence was introduced in 2024, rumors and leaks have hinted at Apple's ambitious plans to create a suite of smart home devices, and it all starts with revamped HomePods. The <a href="https://www.tomsguide.com/home/smart-home/apple-smart-home-hub-we-just-got-bad-news-about-the-release-window">problem has been a lack of a viable AI-powered Siri,</a> which will finally come to fruition thanks to iOS 27.  </p><p>If it actually releases and works as pitched, this year could actually be the start of Apple Home. </p><h3 id="homepad-home-hub">HomePad Home hub</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Maj5caC4eG4rJYyquwrf3L" name="HomePod with screen edit" alt="HomePod with display concept render" src="https://cdn.mos.cms.futurecdn.net/Maj5caC4eG4rJYyquwrf3L.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Yanko Design)</span></figcaption></figure><p>The totally redesigned HomePod would likely get a new name, the HomePad, because it's functionally an iPad stapled to a HomePod speaker. </p><p>Several different iterations of this device have been rumored, including one with a <a href="https://www.tomsguide.com/home/smart-home/apples-next-big-thing-could-be-a-dollar1000-home-robot-ipad-combo">$1,000 robot arm</a>. Currently, rumors claim it will launch in two versions: the iPad/speaker and a <a href="https://www.tomsguide.com/home/smart-home/apple-tipped-to-launch-ai-wall-tablet-to-control-your-home-in-march-2025-everything-we-know-so-far">wall-mounted touchpad</a> similar to the Google Nest Hub or Amazon's Echo Show 8. </p><p>The system would run a new HomeOS, based on watchOS. It could have a FaceTime camera for video calls and presence sensors to initiate smart home routines. The camera might enable face recognition that would display personalized displays based on the user.</p><p>For more check out our <a href="https://www.tomsguide.com/home/smart-home/apple-homepad-rumored-release-date-price-and-all-the-latest-leaks">HomePad rumor roundup</a>.</p><h3 id="homepod-mini-2">HomePod mini 2</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hwxPhQRyonEzSsGSe5nfRN" name="HomePod mini Apple.jpg" alt="Apple HomePod mini" src="https://cdn.mos.cms.futurecdn.net/hwxPhQRyonEzSsGSe5nfRN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Evan Blass/Voice)</span></figcaption></figure><p>Bloomberg's Mark Gurman, long a reporter of the rumored HomePod updates, <a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">believes </a>that a new <a href="https://www.tomsguide.com/reviews/apple-homepod-mini">HomePod mini</a> could launch this September alongside the rumored HomePod Home hub.</p><p>A <a href="https://www.tomsguide.com/home/smart-home/new-apple-tv-and-homepod-mini-reportedly-launching-in-2025-with-apple-networking-chip">new HomePod mini</a> might debut, but it probably won't be as big of an upgrade as the Hub. Most rumors suggest that it will feature a new chip and audio improvements. The current version debuted in 2020, and like the Apple TV 4K, is long overdue for an update.</p><p>Like the HomePad, it will use Siri AI to run smart home routines, though only through voice commands.</p><h2 class="article-body__section" id="section-apple-s-future-products"><span>Apple's future products </span></h2><p>While the Surprise and Shine event will take the headlines next week, Gurman's <a href="https://www.bloomberg.com/news/newsletters/2026-09-06/apple-s-new-ceo-like-cook-pay-package-shows-he-s-going-nowhere-sept-9-event-mtpvpcj1" target="_blank">report</a> reveals a total of 41 devices that Apple could launch over the next three years. </p><p>This includes everything from an OLED iPad mini and M6 Macs to a MacBook Neo refresh and the<a href="https://www.tomsguide.com/phones/iphones/iphone-air-2-rumors-5-biggest-expected-upgrades"> iPhone Air 2</a>. There is even speculation on devices that could launch in 2028 and beyond. We'll get into all that elsewhere but for now, here's what should round out the year.</p><h2 id="other-apple-devices-slated-for-2026">Other Apple devices slated for 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NEFHMiWXieMCYDucFj4vq8" name="iPad mini 7.jpg" alt="iPad mini 7" src="https://cdn.mos.cms.futurecdn.net/NEFHMiWXieMCYDucFj4vq8.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>Outside of new M6 chips, the big updates on this list are all about adding OLED to Apple's Mac lineup. If Apple sticks with its usual schedule, these will launch around October, maybe early November at the latest.</p><ul><li><strong>iPad mini with OLED screen: </strong>Outside of chips, the iPad mini has retained the same design since 2021. <a href="https://www.tomsguide.com/computing/ipads/first-oled-ipad-mini-tipped-for-october-launch-heres-why-im-excited-and-worried">An OLED display</a> would be one of the biggest updates in years. It should making gaming, watching media and general tasks more enjoyable. Though with everything getting more expensive don't expect the same $599 price tag.</li><li><strong>M6 iMac refresh: </strong>We've already seen the M6 chipset in action with the <a href="https://www.tomsguide.com/computing/apple-desktops/apple-is-turbocharging-the-mac-mini-with-its-new-m6-chip-but-the-model-with-an-m5-pro-is-actually-faster">announced M6 Mac mini</a>, which proved efficient. The iMac should get a similar update this fall.</li><li><strong>M6 14-inch MacBook Pro refresh: </strong>Like the iMac, we're only expecting a chip update from the M5 to the M6, but it should prove powerful.</li><li><strong>First MacBooks with touch display and OLED: </strong>Like the iPad mini, MacBooks might finally get a touch display and OLED panel this year. However, it will likely feature the M5 Pro and M5 Max processors, not the M6. Additionally, it might get a new name, <a href="https://www.tomsguide.com/computing/laptops/macbook-ultra-everything-we-know-so-far">MacBook Ultra</a>, and a higher price tag.</li></ul><p>Will you be tuning in to Apple's event? Let me know which device you're most hyped for in the comments.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-exclusive-gurman-on-price-release-date-and-secret-weapon-versus-android">iPhone Ultra exclusive: Gurman on price, release date rumors and 'day one' weapon against Android</a></li><li><a href="https://www.tomsguide.com/computing/mobile-apps/mapquest-is-suddenly-no-4-in-the-apple-app-store-heres-why">'We're not changing it': MapQuest hits No. 1 in App Store after refusing to rename Lake Ontario to Lake America</a></li><li><a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-18-pro-max-unboxed-ahead-of-apple-event-these-could-be-the-final-designs">iPhone Ultra and 18 Pro Max unboxed ahead of Apple event — these could be the final designs</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/phones/iphones/apple-september-event-preview-all-the-new-products-to-expect-at-iphone-18-pro-launch-and-beyond</link>
                                                                            <description>
                            <![CDATA[ With Apple's "Surprise and Shine" event days away, here's everything we expect to see on September 9. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">Ddtg5wKafnD6K8rqUNzdNR</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/4Qqq6ok3k7yMqGWwkBhFdR-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 05 Sep 2026 04:45:00 +0000</pubDate>                                                                                                                                <updated>Mon, 07 Sep 2026 23:35:08 +0000</updated>
                                                                                                                                            <category><![CDATA[iPhones]]></category>
                                                    <category><![CDATA[Airpods]]></category>
                                                    <category><![CDATA[Apple TV]]></category>
                                                    <category><![CDATA[Phones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                    <category><![CDATA[Entertainment]]></category>
                                                    <category><![CDATA[Streaming]]></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.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;br&gt;Send secure tips on Signal: scyo1.55.&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/4Qqq6ok3k7yMqGWwkBhFdR-1280-80.jpg">
                                                            <media:credit><![CDATA[Unbox Therapy, Apple Track, Apple, edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[iPhone 18 Pro, iPhone Ultra, Apple Watch]]></media:description>                                                            <media:text><![CDATA[iPhone 18 Pro, iPhone Ultra, Apple Watch]]></media:text>
                                <media:title type="plain"><![CDATA[iPhone 18 Pro, iPhone Ultra, Apple Watch]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/4Qqq6ok3k7yMqGWwkBhFdR-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">APPLE EVENT LATEST NEWS AND RUMORS</div><div class="fancy_box_body"><p class="fancy-box__body-text"><em><strong>Updated September 7</strong></em></p><p class="fancy-box__body-text">• This is why the A20 Pro cvhip will be such a big deal for iPhone Ultra and 18 Pro<br>•  Here's what you need to know about the new <a data-analytics-id="inline-link" href="https://www.tomsguide.com/home/smart-home/apple-homepad-rumored-release-date-price-and-all-the-latest-leaks">Apple HomePad</a><br>• The <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/iphone-ultra-price-rumors-heres-how-much-you-can-expect-to-pay-for-apples-first-foldable">iPhone Ultra price</a> could be even higher than you expect<br>• Our i<a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/iphone-ultra-exclusive-gurman-on-price-release-date-and-secret-weapon-versus-android">Phone Ultra preview interview</a> has all the details you need to know<br>• <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/apple-event-is-official-for-september-9-iphone-ultra-iphone-18-pro-max-watch-12-and-more">"Surprise and shine"</a> event officially set for Wednesday, September 9</p></div></div><p>Apple's huge "Surprise and Shine" is just days away and the first major event under the leadership of incoming CEO John Ternus' promises to be a big one. And now we have a fairly clear picture of what the company should announce.</p><p>Bloomberg's Mark Gurman has dropped a <a href="https://www.bloomberg.com/news/newsletters/2026-09-06/apple-s-new-ceo-like-cook-pay-package-shows-he-s-going-nowhere-sept-9-event-mtpvpcj1" target="_blank">massive report</a> that outlines everything Apple should release over the next year. And we expect 9 of those new devices to come at the Apple September event, including Apple's first foldable phone in the <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-rumors-everything-you-need-to-know-about-the-foldable-iphone">iPhone Ultra</a>.</p><p>We've got a full breakdown of the devices likely headed your way, from the <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-rumors-everything-we-know-so-far">iPhone 18 Pr</a>o series and <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-12-and-ultra-4-rumors-your-next-apple-watch-could-have-a-ceramic-case">Apple Watch 12</a> to a new HomePad.</p><h3 class="article-body__section" id="section-when-is-the-apple-event"><span>When is the Apple event?</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2066px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="fnjPCS2zVb2TZ4GCnuqwK5" name="Apple suprise and shine" alt="Apple fall event 2026." src="https://cdn.mos.cms.futurecdn.net/fnjPCS2zVb2TZ4GCnuqwK5.jpg" mos="" align="middle" fullscreen="" width="2066" height="1162" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>Apple has slated its "Surprise and shine" event for <strong>Wednesday, September 9 at Apple Park</strong>. Typically, Apple would hold its event on a Tuesday, but this year it's coming right after Labor Day, meaning it's being pushed to Wednesday.</p><p>This has happened before and Apple's reasoning has been that the company doesn't want to ask attendees to travel on the holiday weekend. As an example, the <a href="https://www.tomsguide.com/reviews/iphone-14">iPhone 14</a> launched on Wednesday, September 7, following Labor Day on Monday 5.</p><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/rQHEN3kngZo" allowfullscreen></iframe></div></div><p>The <strong>event is set to take place at 10am PT / 1pm ET / 6pm BST</strong> and will be livestreamed on various social media platforms as well as Apple's own site.</p><p>iPhones generally go up for pre-order the Friday following the Apple event, but this year that's September 11. Gurman has reported that pre-orders will be <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-iphone-18-pro-pre-order-date-just-tipped-heres-when-you-can-get-yours">pushed to Saturday, September 12</a>.</p><p>New CEO John Ternus is expected to host the monster event, and his tenure appears ready to start with a bang.</p><h2 class="article-body__section" id="section-iphone-18-pro-and-pro-max"><span>iPhone 18 Pro and Pro Max</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:2490px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="bc3v2xma2LGpb9mYSNpejc" name="iphone 18 pro render in dark cherry" alt="iphone 18 pro in dark cherry color from macworld" src="https://cdn.mos.cms.futurecdn.net/bc3v2xma2LGpb9mYSNpejc.png" mos="" align="middle" fullscreen="" width="2490" height="1401" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Macworld)</span></figcaption></figure><p>This year, we believe that Apple will only launch the <a href="https://www.tomsguide.com/phones/iphones/iphone-18-rumors-everything-we-know-so-far">iPhone 18 Pro</a> and <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-max-rumors-and-leaks-everything-we-know-so-far">iPhone 18 Pro Max</a> models. The entry-level iPhone 18 and iPhone 18 Plus reportedly <a href="https://www.tomsguide.com/phones/iphones/iphone-18-release-schedule-could-be-changing-but-this-rumor-makes-no-sense">won't debut until next spring</a> alongside the iPhone 18e.</p><p>The design of the Pro models should look similar to last year's iPhone 17 Pro models. However, the Dynamic Island should be smaller, with a 24MP selfie camera. <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-camera-upgrades-everything-we-know-about-the-new-sensor">Rumors suggest the Pro Max</a> model will also gain exclusive access new <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-could-be-the-first-iphone-to-get-a-variable-aperture-why-that-matters">variable aperture lens</a> and might also have a longer 6x zoom.</p><p>Both models should feature Apple's new 2nm <a href="https://www.tomsguide.com/phones/iphones/apple-a20-pro-chip-why-its-such-a-big-deal-for-the-iphone-ultra-and-iphone-18-pro-max">A20 Pro chip</a> and a new C2 modem. A <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-iphone-18-pro-a20-chip-leak-reveals-the-biggest-gpu-ever-inside-an-iphone">recent leak suggests</a> the A20 Pro could feature the largest ever GPU inside an iPhone, meaning better, more efficient performance. Speaking of performance, the <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-max-battery-rumors-it-could-be-a-record-breaking-year-for-apple">latest rumors</a> suggest the iPhone 18 Pro Max could pack a new battery somewhere between 5,391 mAh and 5,567 mAh, and if Apple pushes the silicon's efficiency performance closer to 30%, it could last over 23 hours on a single charge. </p><p>For more, see our <a href="https://www.tomsguide.com/phones/iphones/3-big-reasons-to-buy-iphone-18-pro-max-over-the-iphone-18-pro-this-year">iPhone 18 Pro vs iPhone 18 Pro Max</a> comparison.</p><p>Unfortunately, <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-could-see-absolutely-massive-price-hike-according-to-researchers">analysts have predicted</a> that the new iPhone 18 Pro models could cost between $200 and $250 more than the iPhone 17 Pro family. Here's hoping you have that extra money saved to cover the difference.</p><h3 class="article-body__section" id="section-iphone-ultra"><span>iPhone Ultra</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="g5Ndi778wD3dSpn6XzB5te" name="iPhone Ultra Back AppleTrack-2" alt="iPhone Ultra render shown in hand" src="https://cdn.mos.cms.futurecdn.net/g5Ndi778wD3dSpn6XzB5te.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: AppleTrack)</span></figcaption></figure><p>The biggest announcement of the Apple event is sure to be the debut of the foldable iPhone Ultra.</p><p>An <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-18-pro-max-unboxed-ahead-of-apple-event-these-could-be-the-final-designs">unboxing video</a> from this week revealed a squat passport shape similar to the new <a href="https://www.tomsguide.com/phones/samsung-phones/samsung-galaxy-z-fold-8-review-wide-for-the-win">Galaxy Z Fold 8</a>, with a <a href="https://www.tomsguide.com/phones/iphones/iphone-fold-specs-potentially-revealed-heres-how-it-would-stack-up-against-samsung-galaxy-z-fold-8">7.8-inch foldable display and a 5.5-inch cover screen</a>. Apple has allegedly made a crease-free display, one of the hangs up that stopped the company from making a foldable until now.</p><p>The Ultra could measure between 4.5 and 4.8mm when unfolded, making it one of the thinnest iPhones ever.</p><p>Inside, the Ultra would also feature the <a href="https://www.tomsguide.com/phones/iphones/iphone-fold-tipped-for-a20-pro-chip-apples-first-2nm-processor-to-power-new-foldable-phone">A20 Pro chipset</a> and Apple's in-house C2 modem, plus a Touch ID fingerprint scanner in the power button — in lieu of Face ID. It's supposed to feature a fairly larger 4,883 mAh battery.</p><p>The rear panel should house two 48MP cameras horizontally positioned (wide and ultrawide) and two separate 24MP selfie cameras — one for the cover display and one for the interior screen. </p><p>Apple reportedly delayed releasing a folding iPhone until it could eliminate the crease in the display, but whether it's accomplished this remains to be seen. Even Samsung and Oppo, despite their long lead time, have only <a href="https://www.tomsguide.com/phones/samsung-phones/comparing-galaxy-z-fold-8-vs-oppo-find-n6-crease">had moderate success</a>.</p><p>One downside is that the <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-price-rumors-heres-how-much-you-can-expect-to-pay-for-apples-first-foldable">iPhone Ultra price</a> could be very expensive, costing anywhere from $2,000 to 2,500. And a recent leak suggests that<a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-leakers-reveal-new-details-around-release-and-extremely-limited-availability"> iPhone Ultra may not be immediately available, </a>with only "extremely limited" availability.</p><p>Just keep in mind that the <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-trade-offs-the-biggest-things-youre-not-getting-versus-iphone-18-pro">iPhone Ultra could have a number of trade-offs</a> versus the iPhone 18 Pro, including the lack of a telephoto zoom.</p><h2 class="article-body__section" id="section-airpods-5"><span>AirPods 5</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="2ZeAyG6YEDw3Uu4QoJaXYA" name="Airpods 4.jpg" alt="Airpods 4" src="https://cdn.mos.cms.futurecdn.net/2ZeAyG6YEDw3Uu4QoJaXYA.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>The rumor mill has been excitedly leaking information about the AirPods Ultra, which could <a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know">feature a camera</a> that will feed into Siri to allow it to "see" the world. But according to a <a href="https://www.tomsguide.com/audio/airpods/gurman-ai-airpods-with-cameras-just-tipped-for-2027-launch-in-flurry-of-new-releases">Mark Gurman report</a>, we aren't expecting the <a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-everything-we-know-so-far-about-apples-camera-earbuds">AirPods Ultra</a> until 2027 at the earliest. </p><p>Instead, we should get a slightly upgraded pair of AirPods 5. AppleTrack claims the design will be similar to the current AirPods 4. </p><p>Only two real upgrades have been suggested, including a better chip, volume control on the buds and a heart sensor similar to the Pro models. From a software side, the new AirPods 5 will likely take advantage of the more <a href="https://www.tomsguide.com/phones/iphones/i-spent-a-week-with-siri-ai-here-are-my-honest-pros-and-cons">powerful Siri AI </a>releasing this fall, though what features it gets remains to be seen.</p><h2 class="article-body__section" id="section-apple-watch-12-and-apple-watch-ultra-4"><span>Apple Watch 12 and Apple Watch Ultra 4</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:589px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="fYpaDpUGy9ie37wUcNrzwZ" name="Apple Watch Ultra 3" alt="Apple Watch Ultra 3" src="https://cdn.mos.cms.futurecdn.net/fYpaDpUGy9ie37wUcNrzwZ.jpg" mos="" align="middle" fullscreen="" width="589" height="331" 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>Similar to the AirPods, the leakers have been fairly quiet about the next Apple Watches. We are expecting to see the <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-series-12-everything-we-know-so-far">Apple Watch Series 12</a> and the <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-ultra-4-these-are-the-upgrades-apple-needs-to-make-to-remain-the-smartwatch-king">Apple Watch Ultra 4</a>.</p><p>The designs shouldn't change too much, though Apple might  <a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">offer a ceramic case </a><a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">option </a>for the new watches. The biggest change might be a <a href="https://www.tomsguide.com/wellness/smartwatches/leaked-apple-watch-12-design-change-is-the-opposite-of-an-upgrade-and-that-has-me-worried">new kind of band attachment</a>. This does mean that your old bands might not be compatible with the new watches.</p><p>AppleTrack suggests Apple will put a new S12 chip in the watches with a focus on speed and battery life, though exactly how fast and how much better was not revealed.</p><p>Both watches should get new health and fitness features. We already know that <a href="https://www.tomsguide.com/wellness/smartwatches/watchos-27-all-the-new-features-coming-to-apple-watch-later-this-year#section-watchos-27-new-health-and-fitness-features">watchOS 27</a> will bring new AI features, an updated Workout Buddy and more useful data tracking. Plus, it should feature better integration with the upgraded Siri AI, which we've already seen in the <a href="https://www.tomsguide.com/phones/iphones/ios-27-public-beta-is-now-live-9-features-you-need-to-try-first">iOS 27 beta</a>.</p><h3 class="article-body__section" id="section-apple-tv-4k"><span>Apple TV 4K</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1280px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="35tUhk2U4oNYnKewrYtsxJ" name="apple tv vpn.jpg" alt="Apple TV" src="https://cdn.mos.cms.futurecdn.net/35tUhk2U4oNYnKewrYtsxJ.jpg" mos="" align="middle" fullscreen="" width="1280" height="720" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Lebedev Yury/Shutterstock)</span></figcaption></figure><p>Apple hasn't released a new version of the <a href="https://www.tomsguide.com/reviews/apple-tv-4k-2022">Apple TV 4K</a> since 2022. We're long overdue for an upgrade — even if it's only a more powerful chip for stronger performance. </p><p>Like everything else this year, don't expect new designs. It should look similar to older editions. Rumors have repeatedly suggested that the streaming box will feature the <a href="https://www.tomsguide.com/entertainment/streaming-devices/new-apple-tv-4k-biggest-upgrades-just-leaked-ahead-of-launch">A17 Pro chip</a> — the same as in the <a href="https://www.tomsguide.com/news/a17-pro">iPhone 15 Pro models</a>. It should also get the in-house N1 chip for better Wi-Fi and Bluetooth connectivity.</p><p>Outside of chips, the big upgrade would be Siri AI, which could let you connect the Apple TV as a hub for smart devices. One rumor suggested that Apple might release a <a href="https://www.bloomberg.com/news/newsletters/2024-04-07/apple-aapl-future-products-could-include-robots-and-smart-home-push-lupinmnahttps:/www.bloomberg.com/news/newsletters/2024-04-07/apple-aapl-future-products-could-include-robots-and-smart-home-push-lupinmna" target="_blank">set-top box with a camera,</a> which would allow for FaceTime calls and gesture-based controls.</p><p>AppleTrack suggests that the new Apple TV will be expensive, possibly costlier than the current edition, which is currently <a href="https://www.tomsguide.com/entertainment/apple-tv/apples-price-hikes-finally-made-me-pick-up-the-apple-tv-4k-ive-been-eyeing-for-weeks-and-you-can-still-grab-one-at-the-old-price">priced at $199 and $179 following price hikes</a> this summer.</p><h2 class="article-body__section" id="section-smart-home"><span>Smart Home</span></h2><p>Since Apple Intelligence was introduced in 2024, rumors and leaks have hinted at Apple's ambitious plans to create a suite of smart home devices, and it all starts with revamped HomePods. The <a href="https://www.tomsguide.com/home/smart-home/apple-smart-home-hub-we-just-got-bad-news-about-the-release-window">problem has been a lack of a viable AI-powered Siri,</a> which will finally come to fruition thanks to iOS 27.  </p><p>If it actually releases and works as pitched, this year could actually be the start of Apple Home. </p><h3 id="homepad-home-hub">HomePad Home hub</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Maj5caC4eG4rJYyquwrf3L" name="HomePod with screen edit" alt="HomePod with display concept render" src="https://cdn.mos.cms.futurecdn.net/Maj5caC4eG4rJYyquwrf3L.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Yanko Design)</span></figcaption></figure><p>The totally redesigned HomePod would likely get a new name, the HomePad, because it's functionally an iPad stapled to a HomePod speaker. </p><p>Several different iterations of this device have been rumored, including one with a <a href="https://www.tomsguide.com/home/smart-home/apples-next-big-thing-could-be-a-dollar1000-home-robot-ipad-combo">$1,000 robot arm</a>. Currently, rumors claim it will launch in two versions: the iPad/speaker and a <a href="https://www.tomsguide.com/home/smart-home/apple-tipped-to-launch-ai-wall-tablet-to-control-your-home-in-march-2025-everything-we-know-so-far">wall-mounted touchpad</a> similar to the Google Nest Hub or Amazon's Echo Show 8. </p><p>The system would run a new HomeOS, based on watchOS. It could have a FaceTime camera for video calls and presence sensors to initiate smart home routines. The camera might enable face recognition that would display personalized displays based on the user.</p><p>For more check out our <a href="https://www.tomsguide.com/home/smart-home/apple-homepad-rumored-release-date-price-and-all-the-latest-leaks">HomePad rumor roundup</a>.</p><h3 id="homepod-mini-2">HomePod mini 2</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hwxPhQRyonEzSsGSe5nfRN" name="HomePod mini Apple.jpg" alt="Apple HomePod mini" src="https://cdn.mos.cms.futurecdn.net/hwxPhQRyonEzSsGSe5nfRN.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Evan Blass/Voice)</span></figcaption></figure><p>Bloomberg's Mark Gurman, long a reporter of the rumored HomePod updates, <a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">believes </a>that a new <a href="https://www.tomsguide.com/reviews/apple-homepod-mini">HomePod mini</a> could launch this September alongside the rumored HomePod Home hub.</p><p>A <a href="https://www.tomsguide.com/home/smart-home/new-apple-tv-and-homepod-mini-reportedly-launching-in-2025-with-apple-networking-chip">new HomePod mini</a> might debut, but it probably won't be as big of an upgrade as the Hub. Most rumors suggest that it will feature a new chip and audio improvements. The current version debuted in 2020, and like the Apple TV 4K, is long overdue for an update.</p><p>Like the HomePad, it will use Siri AI to run smart home routines, though only through voice commands.</p><h2 class="article-body__section" id="section-apple-s-future-products"><span>Apple's future products </span></h2><p>While the Surprise and Shine event will take the headlines next week, Gurman's <a href="https://www.bloomberg.com/news/newsletters/2026-09-06/apple-s-new-ceo-like-cook-pay-package-shows-he-s-going-nowhere-sept-9-event-mtpvpcj1" target="_blank">report</a> reveals a total of 41 devices that Apple could launch over the next three years. </p><p>This includes everything from an OLED iPad mini and M6 Macs to a MacBook Neo refresh and the<a href="https://www.tomsguide.com/phones/iphones/iphone-air-2-rumors-5-biggest-expected-upgrades"> iPhone Air 2</a>. There is even speculation on devices that could launch in 2028 and beyond. We'll get into all that elsewhere but for now, here's what should round out the year.</p><h2 id="other-apple-devices-slated-for-2026">Other Apple devices slated for 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NEFHMiWXieMCYDucFj4vq8" name="iPad mini 7.jpg" alt="iPad mini 7" src="https://cdn.mos.cms.futurecdn.net/NEFHMiWXieMCYDucFj4vq8.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>Outside of new M6 chips, the big updates on this list are all about adding OLED to Apple's Mac lineup. If Apple sticks with its usual schedule, these will launch around October, maybe early November at the latest.</p><ul><li><strong>iPad mini with OLED screen: </strong>Outside of chips, the iPad mini has retained the same design since 2021. <a href="https://www.tomsguide.com/computing/ipads/first-oled-ipad-mini-tipped-for-october-launch-heres-why-im-excited-and-worried">An OLED display</a> would be one of the biggest updates in years. It should making gaming, watching media and general tasks more enjoyable. Though with everything getting more expensive don't expect the same $599 price tag.</li><li><strong>M6 iMac refresh: </strong>We've already seen the M6 chipset in action with the <a href="https://www.tomsguide.com/computing/apple-desktops/apple-is-turbocharging-the-mac-mini-with-its-new-m6-chip-but-the-model-with-an-m5-pro-is-actually-faster">announced M6 Mac mini</a>, which proved efficient. The iMac should get a similar update this fall.</li><li><strong>M6 14-inch MacBook Pro refresh: </strong>Like the iMac, we're only expecting a chip update from the M5 to the M6, but it should prove powerful.</li><li><strong>First MacBooks with touch display and OLED: </strong>Like the iPad mini, MacBooks might finally get a touch display and OLED panel this year. However, it will likely feature the M5 Pro and M5 Max processors, not the M6. Additionally, it might get a new name, <a href="https://www.tomsguide.com/computing/laptops/macbook-ultra-everything-we-know-so-far">MacBook Ultra</a>, and a higher price tag.</li></ul><p>Will you be tuning in to Apple's event? Let me know which device you're most hyped for in the comments.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-exclusive-gurman-on-price-release-date-and-secret-weapon-versus-android">iPhone Ultra exclusive: Gurman on price, release date rumors and 'day one' weapon against Android</a></li><li><a href="https://www.tomsguide.com/computing/mobile-apps/mapquest-is-suddenly-no-4-in-the-apple-app-store-heres-why">'We're not changing it': MapQuest hits No. 1 in App Store after refusing to rename Lake Ontario to Lake America</a></li><li><a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-18-pro-max-unboxed-ahead-of-apple-event-these-could-be-the-final-designs">iPhone Ultra and 18 Pro Max unboxed ahead of Apple event — these could be the final designs</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Bose QuietComfort Headphones (2nd Gen) review: Fantastic ANC, but missing one big feature for travelers ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">Bose QuietComfort Headphones (2nd Gen) review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price:</strong> $359<br><strong>Durability: </strong>Not waterproof<br><strong>Connectivity:</strong> Bluetooth 5.4; 3.5mm; USB-C<br><strong>Compatibility:</strong> iOS/Android (with Bose app)<br><strong>Multipoint connectivity: </strong>Yes<br><strong>ANC: </strong>Yes<br><strong>Wear detection: </strong>No<br><strong>Battery life: </strong>24 hours (18 with Immersive Audio)<br><strong>Weight: </strong>8.1 ounces<br><strong>Colors:</strong> Eucalyptus Green; Dewdrop Mint; Rosewood Mauve; White Smoke; Black</p></div></div><p>The Bose QuietComfort Headphones (2nd Gen) aim to deliver premium noise cancellation and comfort for commuters and frequent flyers. Sitting in the premium mid-tier of the category, they replace the first-generation QuietComfort Headphones and bring over standout features like Immersive Audio, from the pricier Ultra series. </p><p>But it's a crowded market, and I can’t confidently say these are the <a href="https://www.tomsguide.com/us/best-noise-cancelling-headphones,review-5566.html"><u>best headphones</u></a> $350 or so can get you when the <a href="https://www.tomsguide.com/reviews/sony-wh-1000xm5"><u>Sony WH-1000XM5</u></a> are permanently discounted for less. The lack of wear detection means you have to manually power them down to save juice, and a 24-hour battery life is merely middle of the pack.</p><p>On the positive side, Bose absolutely nails the comfort and ANC, providing 10 levels of highly effective noise isolation and plush padding that makes wearing them for hours a breeze. The addition of a color-matched 3.5mm-to-USB-C cable for lossless audio input is also a massive win for the variety of ways you can enjoy these cans.</p><p>Find out more in my full Bose QuietComfort Headphones (2nd Gen) review.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-price-and-availability"><span>Bose QuietComfort Headphones (2nd Gen) review: Price and availability</span></h3><ul><li><strong>$359 MSRP but expect discounts</strong></li><li><strong>Competes closely with higher-end Bose models on sale</strong></li></ul><p>The Bose QuietComfort Headphones (2nd Gen) costs <a href="https://www.amazon.com/dp/B0CCZ1SQ8G"><u>$359 at Amazon</u></a>, direct at Bose, and other major retailers. While this still feels like a steep initial asking price, it is highly likely that it will eventually settle closer to $299 during sales events. For context, the original QC Headphones have dropped as low as $229.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="EPvKCqFsoGTttWJRtsMiPg" name="Bose-QuietComfort-Headphones-(Gen 2)--6" alt="Kate Kozuch holding the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/EPvKCqFsoGTttWJRtsMiPg.jpg" mos="" align="middle" fullscreen="" width="2000" height="1124" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Bose's higher-end <a href="https://www.tomsguide.com/audio/over-ear-headphones/bose-quietcomfort-ultra-headphones-2nd-generation-review"><u>QuietComfort Ultra 2nd Gen Headphones</u></a> have also been on sale for as low as $369. Meanwhile, the first-generation QuietComfort Headphones are still available and often discounted to around $250. You have to weigh the price against the newly added features, which I’ll elaborate more on now, starting with design.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-design"><span>Bose QuietComfort Headphones (2nd Gen) review: Design</span></h3><ul><li><strong>Supremely comfortable with plush padding</strong></li><li><strong>Intuitive physical buttons</strong></li><li><strong>Folds into a convenient carrying case</strong></li></ul><p>Aesthetically, the Bose QuietComfort Headphones (2nd Gen) maintain a familiar, somewhat plasticky build, but they are offered in a massive color selection. I tested the light green (officially, Eucalyptus Green) and it adds a fun, seasonal pop to an otherwise traditional frame. </p><p><a href="https://www.tiktok.com/@tomsguide/video/7631561938614390029"><u>Bose has become a master at the color game recently</u></a>, regularly doing splashly color drops across its lineup of audio products, so I’d expect an even greater selection of options coming soon. </p><p>What these headphones might lack in premium metal flair, they more than make up for in sheer wearability. They fold up conveniently into an included carrying case, though I’ve already resorted to hooking them around my luggage when traveling to save space.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xBPcxNKkwhZBAviPwNXrpf" name="Bose-QuietComfort-Headphones-(Gen 2)--11" alt="Bose QuietComfort Headphones (2nd Gen) sitting on a denim jacket" src="https://cdn.mos.cms.futurecdn.net/xBPcxNKkwhZBAviPwNXrpf.jpg" mos="" align="middle" fullscreen="" width="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 headphones are not IP-rated for weatherproofing. This means there is absolutely no guaranteed protection against water or dust. I generally hate when people wear over-ear headphones to the gym, (specifically, AirPods Max) and it's especially risky here. They are not safe for workouts or getting caught out in the rain. Waterproofing over ears is difficult, so typically you need to spend more on something like the $899 Noble Audio FoKus Artemis or $1,250 <a href="https://youtu.be/FIVteoDdo2k"><u>Bang & Olufsen Beoplay H95</u></a> to get an IP-rating.</p><p><strong>Comfort</strong></p><p>Comfort is where these headphones truly shine. The earcups feature just the right amount of plush padding, and Bose extended the padding almost fully around the headband, whereas the previous generation only padded the top quarter. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NBgyituXp8H9KdKMKn4XWf" name="Bose-QuietComfort-Headphones-(Gen 2)--8" alt="Bose QuietComfort Headphones (2nd Gen) held in a hand" src="https://cdn.mos.cms.futurecdn.net/NBgyituXp8H9KdKMKn4XWf.jpg" mos="" align="middle" fullscreen="" width="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 no clamping issues here; speaking from experience on my recent cross-country flights, they are genuinely comfortable to wear for 8-plus hours without experiencing pressure or fatigue. </p><p><strong>Controls</strong></p><p>I love that Bose stuck with physical buttons, as they are overall intuitive and highly satisfying to touch. All playback and volume controls sit on the right earcup, while the power, Bluetooth, and ANC toggles are on the left. Because they are physical, there is no risk of accidentally changing the volume when I brush my hair out of the way or lean against a plane pillow.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZjvcFpscmU2QwJSUckFHhf" name="Bose-QuietComfort-Headphones-(Gen 2)--9" alt="The control buttons on the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/ZjvcFpscmU2QwJSUckFHhf.jpg" mos="" align="middle" fullscreen="" width="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>But there is a major downside: they lack wear detection. With no on-head detection, you have to physically turn them off, or the battery will just continue to drain. Call me crazy, but I firmly believe $300+ headphones should have some version of this feature. </p><p><strong>Connectivity</strong></p><p>These headphones are equipped with Bluetooth 5.4, a notable step up from the previous model's Bluetooth 5.1. It’s still behind the latest <a href="https://www.tomsguide.com/audio/bluetooth-speakers/bluetooth-60-just-revealed-heres-what-that-means-for-you"><u>Bluetooth 6.0</u></a>, though, which other mid-range headphones like the <a href="https://www.tomsguide.com/audio/headphones/jbl-live-780nc-review"><u>JBL Live 780NC</u></a> launched with in 2026.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6L9Ar9YmJCxBgjbJxYp5gf" name="Bose-QuietComfort-Headphones-(Gen 2)--10" alt="The Bluetooth switch on the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/6L9Ar9YmJCxBgjbJxYp5gf.jpg" mos="" align="middle" fullscreen="" width="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>Multipoint connectivity works flawlessly; I enjoyed great auto-playback detection while seamlessly switching between my iPhone and iPad. </p><p>While they do not feature LDAC, they do support USB-C lossless audio input up to 24-bit/48kHz, and Bose thoughtfully includes both color-matched 3.5mm-to-USB-C and USB-C-to-USB-C cables nestled inside the carrying case.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-sound-quality"><span>Bose QuietComfort Headphones (2nd Gen) review: Sound quality</span></h3><ul><li><strong>Bose’s Immersive Audio added</strong></li><li><strong>Lossless USB-C support</strong></li><li><strong>Warm bass but slightly capped treble</strong></li></ul><p>Bose hasn't upgraded the actual drivers here, yet these headphones somehow manage to sound better than the original QC Headphones. The biggest addition is the Immersive Audio feature with head tracking, which was previously exclusive to the Ultra series. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="B68Gbv4rx4QbAbBCqD5Bjf" name="Bose-QuietComfort-Headphones-(Gen 2)--4" alt="Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/B68Gbv4rx4QbAbBCqD5Bjf.jpg" mos="" align="middle" fullscreen="" width="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 listening over Bluetooth, the Bose QuietComfort Headphones (2nd Gen)’s overall profile is warm and engaging. To test the bass and soundstage, I played Milky Chance's <em>Stolen Dance</em>. The intro beats were incredibly punchy. While the soundstage isn't the absolute largest I've heard, the depth and separation during the bass drum build-up in Tracy Chapman’s <em>Fast Car</em> hits beautifully, offering a controlled, thick low-end.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="x7QFkDRrwWmFKGiNMzBvnf" name="Bose-QuietComfort-Headphones-(Gen 2)--2" alt="Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/x7QFkDRrwWmFKGiNMzBvnf.jpg" mos="" align="middle" fullscreen="" width="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>Moving to the mid-tones, I cued up Bon Iver's <em>Wash.</em> to see how the headphones handled instrumental separation. The contrast between the layered vocals and the piano had a haunting, distinct clarity. Mid-tones are powerful and well-defined, and listening to David Bowie’s <em>Heroes</em> revealed excellent instrumental distinctions that kept his voice sounding true to the original recording.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1553px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="Dy4hyTabZWPpuRHc9E2dQH" name="Bose-QuietComfort-Headphones-(Gen 2)-LIST3" alt="Tom's Guide editor Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/Dy4hyTabZWPpuRHc9E2dQH.jpg" mos="" align="middle" fullscreen="" width="1553" height="874" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>However, the treble and higher vocals can feel a bit restrained. When testing Michael Jackson’s <em>P.Y.T</em>., the high notes were slightly capped, reinforcing the fact that the highs here don't have quite the same sense of openness. Tracy Chapman's angelic voice on <em>Fast Car</em> has definitely sounded a bit brighter on other high-end models, establishing that while these cans offer great bass, they are somewhat limited on the highs.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-app"><span>Bose QuietComfort Headphones (2nd Gen) review: App</span></h3><ul><li><strong>Clean and easy to navigate</strong></li><li><strong>10 levels of ANC adjustment</strong></li><li><strong>Controls for Immersive Audio</strong></li></ul><p>The companion Bose app is simple, professional and essential for getting the most out of these headphones. Available on iOS and Android, it allows you to tweak connections, check battery life, and apply basic EQ tailoring. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uPPQt966aJqbRGoffF48Qg" name="Bose-QuietComfort-Headphones-(Gen 2)--12" alt="The app for the  the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/uPPQt966aJqbRGoffF48Qg.jpg" mos="" align="middle" fullscreen="" width="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 also found a little easter egg that suggests inventive names for your headphones based on the color, if you’d like them labeled as something other than the comparatively-sterile product name.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-anc"><span>Bose QuietComfort Headphones (2nd Gen) review: ANC</span></h3><ul><li><strong>Incredibly effective isolation</strong></li><li><strong>10 adjustable levels</strong></li><li><strong>Immersive audio modes</strong></li></ul><p>Bose continues to be one of the best in the business when it comes to ANC, and the QuietComfort Headphones (2nd Gen) easily earn their name. The ANC here is next-level effective. While testing on flights, I felt completely tuned out from the roaring engines and cabin chatter. I even switched between these, the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review"><u>Sony WH-1000XM6</u></a>, and the <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review"><u>Apple AirPods Max 2</u></a>, and found the Bose to be noticeably stronger at blocking out environmental noise.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TL86bx5H7f6r5cFqwoiHif" name="Bose-QuietComfort-Headphones-(Gen 2)--13" alt="Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/TL86bx5H7f6r5cFqwoiHif.jpg" mos="" align="middle" fullscreen="" width="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>While the app offers Bose’s signature 10 levels of ANC adjustment, the implementation is so subtle that I rarely wanted anything less than full effectiveness. </p><p>The added Immersive Audio feature offers excellent "Still" and "Motion" modes, providing a spatial audio experience that acts as a fantastic alternative to standard stereo listening. People are usually torn on this kind of feature, but I’ve personally liked Bose’s implementation of it since I tested the <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds"><u>Bose Ultra Open Earbuds</u></a>.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-call-quality"><span>Bose QuietComfort Headphones (2nd Gen) review: Call quality</span></h3><ul><li><strong>24 hours maximum</strong></li><li><strong>Drops to 18 hours with ANC and Immersive Audio</strong></li><li><strong>Middle of the pack performance</strong></li></ul><p>Bose rates the Bose QuietComfort Headphones (2nd Gen)<strong> </strong>battery life at up to 24 hours on a single charge, which drops to up to 18 hours if you have both noise canceling and Immersive Audio activated. In real terms, this means you can get through a few days of commuting or a very long international flight, but it sits squarely in the middle of the pack. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7D9vM9HmhF5JcQjU3rwVYf" name="Bose-QuietComfort-Headphones-(Gen 2)--5" alt="Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/7D9vM9HmhF5JcQjU3rwVYf.jpg" mos="" align="middle" fullscreen="" width="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 comparison, the Ultra version gets up to 30 hours, and rivals like the <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review"><u>Marshall Monitor III</u></a> or <a href="https://www.tomsguide.com/audio/headphones/cmf-by-nothing-headphone-pro-review"><u>CMF Headphone Pro</u></a> can reach up to 100 hours.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pb2tMW8PokvRBGPewcZFqf" name="Bose-QuietComfort-Headphones-(Gen 2)--7" alt="Kate Kozuch holding the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/pb2tMW8PokvRBGPewcZFqf.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In my testing, the battery drained exactly as expected based on Bose's claims. I used them heavily with full ANC and Immersive Audio engaged, and the resulting 18 hours of playback felt a bit limiting if you're someone who often forgets to charge their devices. The lack of wear detection exacerbates this issue, as leaving them powered on while fumbling to disembark will quickly eat through whatever juice you have left.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-verdict"><span>Bose QuietComfort Headphones (2nd Gen) review: Verdict</span></h3><p>The Bose QuietComfort Headphones (2nd Gen) are an incredibly comfortable set of travel cans that successfully integrate the excellent Immersive Audio feature from the brand's flagship lineup. They provide some of the absolute best noise cancellation on the market, easily hushing noisy plane cabins, and the tactile physical buttons are a joy to use when you need to make quick adjustments on the fly.</p><p>But the lack of wear detection is a frustrating omission that requires you to babysit the power button, and a 24-hour battery life feels quite average given the $359 price tag when compared to modern stamina champions. Additionally, the lack of an IP rating means you'll want to keep these strictly for commuting and desk work rather than gym sessions.</p><p>Ultimately, if you prioritize wearing comfort, physical controls, and world-class ANC over battery longevity, these headphones are a solid travel companion.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/headphones/bose-quietcomfort-headphones-2nd-gen-review</link>
                                                                            <description>
                            <![CDATA[ The Bose QC 2nd Gen add Immersive Audio and USB-C support to an incredibly comfortable frame, but they lack wear detection and top-tier battery life. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">9Qv93Qfkjnp5A5ynDRgdyn</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/Dy4hyTabZWPpuRHc9E2dQH-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 04 Sep 2026 17:43:28 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Headphones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Kate Kozuch ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xAVUdx6Qtp3SzugnnfNYsL.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/Dy4hyTabZWPpuRHc9E2dQH-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Tom&#039;s Guide editor Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)]]></media:description>                                                            <media:text><![CDATA[Tom&#039;s Guide editor Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)]]></media:text>
                                <media:title type="plain"><![CDATA[Tom&#039;s Guide editor Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/Dy4hyTabZWPpuRHc9E2dQH-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">Bose QuietComfort Headphones (2nd Gen) review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price:</strong> $359<br><strong>Durability: </strong>Not waterproof<br><strong>Connectivity:</strong> Bluetooth 5.4; 3.5mm; USB-C<br><strong>Compatibility:</strong> iOS/Android (with Bose app)<br><strong>Multipoint connectivity: </strong>Yes<br><strong>ANC: </strong>Yes<br><strong>Wear detection: </strong>No<br><strong>Battery life: </strong>24 hours (18 with Immersive Audio)<br><strong>Weight: </strong>8.1 ounces<br><strong>Colors:</strong> Eucalyptus Green; Dewdrop Mint; Rosewood Mauve; White Smoke; Black</p></div></div><p>The Bose QuietComfort Headphones (2nd Gen) aim to deliver premium noise cancellation and comfort for commuters and frequent flyers. Sitting in the premium mid-tier of the category, they replace the first-generation QuietComfort Headphones and bring over standout features like Immersive Audio, from the pricier Ultra series. </p><p>But it's a crowded market, and I can’t confidently say these are the <a href="https://www.tomsguide.com/us/best-noise-cancelling-headphones,review-5566.html"><u>best headphones</u></a> $350 or so can get you when the <a href="https://www.tomsguide.com/reviews/sony-wh-1000xm5"><u>Sony WH-1000XM5</u></a> are permanently discounted for less. The lack of wear detection means you have to manually power them down to save juice, and a 24-hour battery life is merely middle of the pack.</p><p>On the positive side, Bose absolutely nails the comfort and ANC, providing 10 levels of highly effective noise isolation and plush padding that makes wearing them for hours a breeze. The addition of a color-matched 3.5mm-to-USB-C cable for lossless audio input is also a massive win for the variety of ways you can enjoy these cans.</p><p>Find out more in my full Bose QuietComfort Headphones (2nd Gen) review.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-price-and-availability"><span>Bose QuietComfort Headphones (2nd Gen) review: Price and availability</span></h3><ul><li><strong>$359 MSRP but expect discounts</strong></li><li><strong>Competes closely with higher-end Bose models on sale</strong></li></ul><p>The Bose QuietComfort Headphones (2nd Gen) costs <a href="https://www.amazon.com/dp/B0CCZ1SQ8G"><u>$359 at Amazon</u></a>, direct at Bose, and other major retailers. While this still feels like a steep initial asking price, it is highly likely that it will eventually settle closer to $299 during sales events. For context, the original QC Headphones have dropped as low as $229.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="EPvKCqFsoGTttWJRtsMiPg" name="Bose-QuietComfort-Headphones-(Gen 2)--6" alt="Kate Kozuch holding the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/EPvKCqFsoGTttWJRtsMiPg.jpg" mos="" align="middle" fullscreen="" width="2000" height="1124" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Bose's higher-end <a href="https://www.tomsguide.com/audio/over-ear-headphones/bose-quietcomfort-ultra-headphones-2nd-generation-review"><u>QuietComfort Ultra 2nd Gen Headphones</u></a> have also been on sale for as low as $369. Meanwhile, the first-generation QuietComfort Headphones are still available and often discounted to around $250. You have to weigh the price against the newly added features, which I’ll elaborate more on now, starting with design.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-design"><span>Bose QuietComfort Headphones (2nd Gen) review: Design</span></h3><ul><li><strong>Supremely comfortable with plush padding</strong></li><li><strong>Intuitive physical buttons</strong></li><li><strong>Folds into a convenient carrying case</strong></li></ul><p>Aesthetically, the Bose QuietComfort Headphones (2nd Gen) maintain a familiar, somewhat plasticky build, but they are offered in a massive color selection. I tested the light green (officially, Eucalyptus Green) and it adds a fun, seasonal pop to an otherwise traditional frame. </p><p><a href="https://www.tiktok.com/@tomsguide/video/7631561938614390029"><u>Bose has become a master at the color game recently</u></a>, regularly doing splashly color drops across its lineup of audio products, so I’d expect an even greater selection of options coming soon. </p><p>What these headphones might lack in premium metal flair, they more than make up for in sheer wearability. They fold up conveniently into an included carrying case, though I’ve already resorted to hooking them around my luggage when traveling to save space.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xBPcxNKkwhZBAviPwNXrpf" name="Bose-QuietComfort-Headphones-(Gen 2)--11" alt="Bose QuietComfort Headphones (2nd Gen) sitting on a denim jacket" src="https://cdn.mos.cms.futurecdn.net/xBPcxNKkwhZBAviPwNXrpf.jpg" mos="" align="middle" fullscreen="" width="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 headphones are not IP-rated for weatherproofing. This means there is absolutely no guaranteed protection against water or dust. I generally hate when people wear over-ear headphones to the gym, (specifically, AirPods Max) and it's especially risky here. They are not safe for workouts or getting caught out in the rain. Waterproofing over ears is difficult, so typically you need to spend more on something like the $899 Noble Audio FoKus Artemis or $1,250 <a href="https://youtu.be/FIVteoDdo2k"><u>Bang & Olufsen Beoplay H95</u></a> to get an IP-rating.</p><p><strong>Comfort</strong></p><p>Comfort is where these headphones truly shine. The earcups feature just the right amount of plush padding, and Bose extended the padding almost fully around the headband, whereas the previous generation only padded the top quarter. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NBgyituXp8H9KdKMKn4XWf" name="Bose-QuietComfort-Headphones-(Gen 2)--8" alt="Bose QuietComfort Headphones (2nd Gen) held in a hand" src="https://cdn.mos.cms.futurecdn.net/NBgyituXp8H9KdKMKn4XWf.jpg" mos="" align="middle" fullscreen="" width="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 no clamping issues here; speaking from experience on my recent cross-country flights, they are genuinely comfortable to wear for 8-plus hours without experiencing pressure or fatigue. </p><p><strong>Controls</strong></p><p>I love that Bose stuck with physical buttons, as they are overall intuitive and highly satisfying to touch. All playback and volume controls sit on the right earcup, while the power, Bluetooth, and ANC toggles are on the left. Because they are physical, there is no risk of accidentally changing the volume when I brush my hair out of the way or lean against a plane pillow.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZjvcFpscmU2QwJSUckFHhf" name="Bose-QuietComfort-Headphones-(Gen 2)--9" alt="The control buttons on the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/ZjvcFpscmU2QwJSUckFHhf.jpg" mos="" align="middle" fullscreen="" width="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>But there is a major downside: they lack wear detection. With no on-head detection, you have to physically turn them off, or the battery will just continue to drain. Call me crazy, but I firmly believe $300+ headphones should have some version of this feature. </p><p><strong>Connectivity</strong></p><p>These headphones are equipped with Bluetooth 5.4, a notable step up from the previous model's Bluetooth 5.1. It’s still behind the latest <a href="https://www.tomsguide.com/audio/bluetooth-speakers/bluetooth-60-just-revealed-heres-what-that-means-for-you"><u>Bluetooth 6.0</u></a>, though, which other mid-range headphones like the <a href="https://www.tomsguide.com/audio/headphones/jbl-live-780nc-review"><u>JBL Live 780NC</u></a> launched with in 2026.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6L9Ar9YmJCxBgjbJxYp5gf" name="Bose-QuietComfort-Headphones-(Gen 2)--10" alt="The Bluetooth switch on the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/6L9Ar9YmJCxBgjbJxYp5gf.jpg" mos="" align="middle" fullscreen="" width="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>Multipoint connectivity works flawlessly; I enjoyed great auto-playback detection while seamlessly switching between my iPhone and iPad. </p><p>While they do not feature LDAC, they do support USB-C lossless audio input up to 24-bit/48kHz, and Bose thoughtfully includes both color-matched 3.5mm-to-USB-C and USB-C-to-USB-C cables nestled inside the carrying case.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-sound-quality"><span>Bose QuietComfort Headphones (2nd Gen) review: Sound quality</span></h3><ul><li><strong>Bose’s Immersive Audio added</strong></li><li><strong>Lossless USB-C support</strong></li><li><strong>Warm bass but slightly capped treble</strong></li></ul><p>Bose hasn't upgraded the actual drivers here, yet these headphones somehow manage to sound better than the original QC Headphones. The biggest addition is the Immersive Audio feature with head tracking, which was previously exclusive to the Ultra series. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="B68Gbv4rx4QbAbBCqD5Bjf" name="Bose-QuietComfort-Headphones-(Gen 2)--4" alt="Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/B68Gbv4rx4QbAbBCqD5Bjf.jpg" mos="" align="middle" fullscreen="" width="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 listening over Bluetooth, the Bose QuietComfort Headphones (2nd Gen)’s overall profile is warm and engaging. To test the bass and soundstage, I played Milky Chance's <em>Stolen Dance</em>. The intro beats were incredibly punchy. While the soundstage isn't the absolute largest I've heard, the depth and separation during the bass drum build-up in Tracy Chapman’s <em>Fast Car</em> hits beautifully, offering a controlled, thick low-end.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="x7QFkDRrwWmFKGiNMzBvnf" name="Bose-QuietComfort-Headphones-(Gen 2)--2" alt="Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/x7QFkDRrwWmFKGiNMzBvnf.jpg" mos="" align="middle" fullscreen="" width="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>Moving to the mid-tones, I cued up Bon Iver's <em>Wash.</em> to see how the headphones handled instrumental separation. The contrast between the layered vocals and the piano had a haunting, distinct clarity. Mid-tones are powerful and well-defined, and listening to David Bowie’s <em>Heroes</em> revealed excellent instrumental distinctions that kept his voice sounding true to the original recording.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1553px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="Dy4hyTabZWPpuRHc9E2dQH" name="Bose-QuietComfort-Headphones-(Gen 2)-LIST3" alt="Tom's Guide editor Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/Dy4hyTabZWPpuRHc9E2dQH.jpg" mos="" align="middle" fullscreen="" width="1553" height="874" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>However, the treble and higher vocals can feel a bit restrained. When testing Michael Jackson’s <em>P.Y.T</em>., the high notes were slightly capped, reinforcing the fact that the highs here don't have quite the same sense of openness. Tracy Chapman's angelic voice on <em>Fast Car</em> has definitely sounded a bit brighter on other high-end models, establishing that while these cans offer great bass, they are somewhat limited on the highs.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-app"><span>Bose QuietComfort Headphones (2nd Gen) review: App</span></h3><ul><li><strong>Clean and easy to navigate</strong></li><li><strong>10 levels of ANC adjustment</strong></li><li><strong>Controls for Immersive Audio</strong></li></ul><p>The companion Bose app is simple, professional and essential for getting the most out of these headphones. Available on iOS and Android, it allows you to tweak connections, check battery life, and apply basic EQ tailoring. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uPPQt966aJqbRGoffF48Qg" name="Bose-QuietComfort-Headphones-(Gen 2)--12" alt="The app for the  the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/uPPQt966aJqbRGoffF48Qg.jpg" mos="" align="middle" fullscreen="" width="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 also found a little easter egg that suggests inventive names for your headphones based on the color, if you’d like them labeled as something other than the comparatively-sterile product name.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-anc"><span>Bose QuietComfort Headphones (2nd Gen) review: ANC</span></h3><ul><li><strong>Incredibly effective isolation</strong></li><li><strong>10 adjustable levels</strong></li><li><strong>Immersive audio modes</strong></li></ul><p>Bose continues to be one of the best in the business when it comes to ANC, and the QuietComfort Headphones (2nd Gen) easily earn their name. The ANC here is next-level effective. While testing on flights, I felt completely tuned out from the roaring engines and cabin chatter. I even switched between these, the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review"><u>Sony WH-1000XM6</u></a>, and the <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review"><u>Apple AirPods Max 2</u></a>, and found the Bose to be noticeably stronger at blocking out environmental noise.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TL86bx5H7f6r5cFqwoiHif" name="Bose-QuietComfort-Headphones-(Gen 2)--13" alt="Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/TL86bx5H7f6r5cFqwoiHif.jpg" mos="" align="middle" fullscreen="" width="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>While the app offers Bose’s signature 10 levels of ANC adjustment, the implementation is so subtle that I rarely wanted anything less than full effectiveness. </p><p>The added Immersive Audio feature offers excellent "Still" and "Motion" modes, providing a spatial audio experience that acts as a fantastic alternative to standard stereo listening. People are usually torn on this kind of feature, but I’ve personally liked Bose’s implementation of it since I tested the <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds"><u>Bose Ultra Open Earbuds</u></a>.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-call-quality"><span>Bose QuietComfort Headphones (2nd Gen) review: Call quality</span></h3><ul><li><strong>24 hours maximum</strong></li><li><strong>Drops to 18 hours with ANC and Immersive Audio</strong></li><li><strong>Middle of the pack performance</strong></li></ul><p>Bose rates the Bose QuietComfort Headphones (2nd Gen)<strong> </strong>battery life at up to 24 hours on a single charge, which drops to up to 18 hours if you have both noise canceling and Immersive Audio activated. In real terms, this means you can get through a few days of commuting or a very long international flight, but it sits squarely in the middle of the pack. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7D9vM9HmhF5JcQjU3rwVYf" name="Bose-QuietComfort-Headphones-(Gen 2)--5" alt="Kate Kozuch wearing the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/7D9vM9HmhF5JcQjU3rwVYf.jpg" mos="" align="middle" fullscreen="" width="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 comparison, the Ultra version gets up to 30 hours, and rivals like the <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review"><u>Marshall Monitor III</u></a> or <a href="https://www.tomsguide.com/audio/headphones/cmf-by-nothing-headphone-pro-review"><u>CMF Headphone Pro</u></a> can reach up to 100 hours.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pb2tMW8PokvRBGPewcZFqf" name="Bose-QuietComfort-Headphones-(Gen 2)--7" alt="Kate Kozuch holding the Bose QuietComfort Headphones (2nd Gen)" src="https://cdn.mos.cms.futurecdn.net/pb2tMW8PokvRBGPewcZFqf.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In my testing, the battery drained exactly as expected based on Bose's claims. I used them heavily with full ANC and Immersive Audio engaged, and the resulting 18 hours of playback felt a bit limiting if you're someone who often forgets to charge their devices. The lack of wear detection exacerbates this issue, as leaving them powered on while fumbling to disembark will quickly eat through whatever juice you have left.</p><h3 class="article-body__section" id="section-bose-quietcomfort-headphones-2nd-gen-review-verdict"><span>Bose QuietComfort Headphones (2nd Gen) review: Verdict</span></h3><p>The Bose QuietComfort Headphones (2nd Gen) are an incredibly comfortable set of travel cans that successfully integrate the excellent Immersive Audio feature from the brand's flagship lineup. They provide some of the absolute best noise cancellation on the market, easily hushing noisy plane cabins, and the tactile physical buttons are a joy to use when you need to make quick adjustments on the fly.</p><p>But the lack of wear detection is a frustrating omission that requires you to babysit the power button, and a 24-hour battery life feels quite average given the $359 price tag when compared to modern stamina champions. Additionally, the lack of an IP rating means you'll want to keep these strictly for commuting and desk work rather than gym sessions.</p><p>Ultimately, if you prioritize wearing comfort, physical controls, and world-class ANC over battery longevity, these headphones are a solid travel companion.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Look no further for stylish sound in compact spaces — I’ve been testing the Kanto UKI speakers and I’m impressed ]]></title>
                                                                                                <dc:content><![CDATA[ <p>If you’re looking for some of the <a href="https://www.tomsguide.com/us/best-computer-speakers,review-6355.html">best computer speakers</a> to upgrade your desk setup, then look no further than the Kanto UKI bookshelf speakers. Though compact they may be, quiet they are not, with a room-filling sound that excellently replicates mids and treble. </p><p>The controls are simple to get to grips with, and the diverse range of connectivity options means you can use them for everything. From wirelessly streaming from your phone to connecting to a turntable, these speakers are incredibly versatile. </p><p>On their own, the speakers struggle to replicate bass and have a narrow soundstage, but these issues can be fixed with additional purchases of a sub and speaker stands. To find out more, you can keep reading this full Kanto UKI review. </p><h2 class="article-body__section" id="section-kanto-uki-review-specs"><span>Kanto UKI 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/Kanto-Bluetooth-Intelligent-Integration-Headphone/dp/B0FSTD2S4M" target="_blank" rel="nofollow">$269</a> / <a href="https://www.amazon.co.uk/Kanto-Bluetooth-Intelligent-Integration-Headphone/dp/B0FSVVT6GL" target="_blank" rel="nofollow">£262</a></p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3, USB-C, RCA, 3.5mm out</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>80 Hz - 22kHz</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions (per speaker)</strong></p></td><td  ><p>6.5 x 4.4 x 4.3 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.2 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Onyx, Chalk, Cornflower, Sage, Pumice</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-kanto-uki-review-price-availability"><span>Kanto UKI review: Price & availability</span></h2><p>The Kanto UKI bookshelf speakers are available for <a href="https://www.amazon.com/Kanto-Bluetooth-Intelligent-Integration-Headphone/dp/B0FSTD2S4M" target="_blank" rel="nofollow">$269 from Amazon U.S</a>. and <a href="https://www.amazon.co.uk/Kanto-Bluetooth-Intelligent-Integration-Headphone/dp/B0FSVVT6GL" target="_blank" rel="nofollow">£262 from Amazon U.K.</a> The speakers come in five different colors, including Onyx, Chalk, Cornflower, Pumice and Sage, which I tested. </p><p>Kanto also offers the <a href="https://www.tomsguide.com/audio/speakers/kanto-ora">Kanto Ora</a> bookshelf speakers for the higher price of $349. These speakers only come in black, but they offer a wider soundstage and studio sound for music production or mixing. There are also the <a href="https://www.tomsguide.com/audio/kanto-ora4-review">Kanto Ora4</a> for $399, which are the same as the Ora but powered. </p><p>A cheaper way to upgrade your desk setup is the <a href="https://www.tomsguide.com/audio/speakers/jbl-104-bt">JBL 104-BT</a> speakers for $199. They have a wide sound but struggle in the low end. I personally don’t think they look as good as the Kanto speakers, but they do the job just fine. </p><p>If you’re short on space and need to save some space (and money), the <a href="https://www.tomsguide.com/audio/speakers/creative-pebble-pro-review">Creative Pebble Pro </a>speakers cost just $69. The speakers have powerful bass, but I prefer the soundstage of the Kanto UKI speakers. </p><h2 class="article-body__section" id="section-kanto-uki-review-design"><span>Kanto UKI review: Design </span></h2><p>The Kanto UKI speakers are an undeniably stylish duo that has brightened up my desk. I tested the Sage colorway of the speakers, and since my room is decorated with a lot of green furniture, they fit right in. </p><p>Each of the speakers measures 6.5 x 4.4 x 4.3 inches, so they’re very compact. They’re smaller than the <a href="https://www.tomsguide.com/audio/speakers/edifier-r1280t-review">Edifier R1280T</a> speakers ($149), which measure 9.2 x 7.7 x 5.7 inches. If you’re short on space, the Kanto speakers are a no-brainer. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CXXy9yLH4MsajcHDK7zLUC" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/CXXy9yLH4MsajcHDK7zLUC.jpg" 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>They are made from partially recycled hard plastic and feel very sturdy. The front of each speaker has an exposed tweeter and woofer. </p><p>At the back of each speaker, there’s a thread for mounting it to the <a href="https://kantoaudio.com/speaker-accessories/su2/" target="_blank" rel="nofollow">Kanto SU2 speaker stands, which are available for $29</a>. This addition will lift the speakers off the desk, giving you more space and reducing surface reflections, enhancing the quality of your playback. Adding these stands made the listening experience ten times better, and I recommend this addition to anyone who buys these speakers. </p><h3 id="controls-4">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kaKAQEx5iBXcnZ2JvSVfHC" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/kaKAQEx5iBXcnZ2JvSVfHC.jpg" 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 controls on the speakers are very minimal and simplistic, but that makes the experience straightforward. They remind me of the ones I had with my family PC in the 2000s. There's one knob on the front of the main speaker that you press to switch connectivity options and turn to adjust volume. </p><p>At the back of the main speaker, you’ll find the Bluetooth button and a small left/right switch. By toggling this switch, you can switch between the left and right output speakers, meaning you can arrange them however you like on your desk. This made it easy for me to arrange my setup according to where the power outlets in the room are. </p><h3 id="connectivity-4">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="obHgAU7Q2Y8nDJRttHugqC" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/obHgAU7Q2Y8nDJRttHugqC.jpg" 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>At the back of the main speaker, you’ll see all of the ports for the different connectivity options. You can listen to music using Bluetooth 5.3, USB-C or RCA inputs, giving you versatility to use them with computers, phone streaming, or hooked up to a record player. There is also a 3.5mm headphone output port at the front of the speaker. </p><p>The speakers need to be connected to power when in use. There’s a six-foot four-pin speaker connector included in the box to connect the left and right speakers. </p><h2 class="article-body__section" id="section-kanto-uki-review-sound-quality"><span>Kanto UKI review: Sound quality</span></h2><p>I listened using the Kanto UKI speakers connected to my computer via USB-C. I’ve enjoyed listening to my music with the bookshelf speakers every day. Overall, the sound from these speakers is very warm, and the reproduction feels very natural. On their own, they’re perfect for casual listening but lack a wide soundstage; however, when I added the Kanto SU2 Desktop Stands to the setup, the sound was transformed. </p><p>When speakers are placed directly onto a surface like your desk, the sound can be subject to reflection. This means that they begin to sound muffled. But with the addition of speaker stands that can direct the sound upwards, this muffled sound is reduced, and sound reflection diminishes. These stands are a saving grace for these speakers. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CbGGTy7EtwGSTFkSBEu2gC" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/CbGGTy7EtwGSTFkSBEu2gC.jpg" 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 the bass performance, I listened to <em>Nightmare Tripping </em>by Don Broco. This song relies heavily on the deep bassline, but that wasn’t replicated very well here. Some of the depth was lost, with more focus being put on the vocals. But this is to be expected with these speakers as they bottom out at 80Hz and are primarily designed for detailed listening, prioritizing the mids performance. But a deep bass performance is still achievable if that’s what you’re looking for, as you can pair the speakers with a small sub offered by <a href="https://www.amazon.com/Kanto-Powered-Subwoofer-Accurate-Ultra-Compact/dp/B0FGJTV73D" target="_blank" rel="nofollow">Kanto, such as the Sub6 for $299</a>.</p><p>The mid-performance was impressive. I listened to <em>Wheels</em> by the Foo Fighters. This song is country-esque (for the Foos) and features powerful drums and a bright electric guitar. The drums were well defined throughout the song, and the electric guitar also sounded very clean.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="F6sbJftqYFz7T64gec6rAD" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/F6sbJftqYFz7T64gec6rAD.jpg" 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>Finally, I listened to <em>Next To Me</em> by Kingfishr to test the treble and vocal performance. This song is soft and acoustic, lending itself to showcasing the more delicate performance of the speakers. </p><p>The vocals and harmonies throughout the song were clear with good separation. The guitar and harmonica also showed off the bright sound of the speakers and the warmth they can replicate.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9nCAtLKCBqRE62urcLUj6C" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/9nCAtLKCBqRE62urcLUj6C.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also used the speakers while watching Invincible, an action TV show, and was impressed by the level of detail and how the sound effects moved between the speakers. </p><h2 class="article-body__section" id="section-kanto-uki-review-verdict"><span>Kanto UKI review: Verdict</span></h2><p>The Kanto UKI bookshelf speakers have been a great addition to my desk setup. I can listen to them all day and know I’ll get an enjoyable, consistent sound. The simple controls make it easy to make quick volume adjustments or switch over connectivity. </p><p>The diverse selection of connectivity options is ideal if you want to use the speakers connected to a PC, wirelessly to your phone, or with a more bespoke setup with a turntable. The small size means they’re easy to store, and the cute design and color options are the cherry on top. </p><p>Unfortunately, these speakers struggle with deep bass tracks, but you could spend a little extra on the Kanto Ora instead if that bothers you. The narrow soundstage also won’t be ideal for anyone looking for desktop speakers to support music production or mixing. With that said, these speakers are ideal for casual, everyday listening while working. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/speakers/kanto-uki-review</link>
                                                                            <description>
                            <![CDATA[ The Kanto UKI bookshelf speakers are now a staple on my desk for their great sound and cute looks, but additional purchases make them shine. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">MSFNBe5xW9BjxifcEbW5CR</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/9fC6MnCfrez6EubdcKbjDD-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 04 Sep 2026 14:04:25 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Speakers]]></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.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/9fC6MnCfrez6EubdcKbjDD-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Kanto UKI bookshelf speakers in sage photographed in front of a blue background]]></media:description>                                                            <media:text><![CDATA[Kanto UKI bookshelf speakers in sage photographed in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[Kanto UKI bookshelf speakers in sage photographed in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/9fC6MnCfrez6EubdcKbjDD-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you’re looking for some of the <a href="https://www.tomsguide.com/us/best-computer-speakers,review-6355.html">best computer speakers</a> to upgrade your desk setup, then look no further than the Kanto UKI bookshelf speakers. Though compact they may be, quiet they are not, with a room-filling sound that excellently replicates mids and treble. </p><p>The controls are simple to get to grips with, and the diverse range of connectivity options means you can use them for everything. From wirelessly streaming from your phone to connecting to a turntable, these speakers are incredibly versatile. </p><p>On their own, the speakers struggle to replicate bass and have a narrow soundstage, but these issues can be fixed with additional purchases of a sub and speaker stands. To find out more, you can keep reading this full Kanto UKI review. </p><h2 class="article-body__section" id="section-kanto-uki-review-specs"><span>Kanto UKI 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/Kanto-Bluetooth-Intelligent-Integration-Headphone/dp/B0FSTD2S4M" target="_blank" rel="nofollow">$269</a> / <a href="https://www.amazon.co.uk/Kanto-Bluetooth-Intelligent-Integration-Headphone/dp/B0FSVVT6GL" target="_blank" rel="nofollow">£262</a></p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3, USB-C, RCA, 3.5mm out</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>80 Hz - 22kHz</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions (per speaker)</strong></p></td><td  ><p>6.5 x 4.4 x 4.3 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.2 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Onyx, Chalk, Cornflower, Sage, Pumice</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-kanto-uki-review-price-availability"><span>Kanto UKI review: Price & availability</span></h2><p>The Kanto UKI bookshelf speakers are available for <a href="https://www.amazon.com/Kanto-Bluetooth-Intelligent-Integration-Headphone/dp/B0FSTD2S4M" target="_blank" rel="nofollow">$269 from Amazon U.S</a>. and <a href="https://www.amazon.co.uk/Kanto-Bluetooth-Intelligent-Integration-Headphone/dp/B0FSVVT6GL" target="_blank" rel="nofollow">£262 from Amazon U.K.</a> The speakers come in five different colors, including Onyx, Chalk, Cornflower, Pumice and Sage, which I tested. </p><p>Kanto also offers the <a href="https://www.tomsguide.com/audio/speakers/kanto-ora">Kanto Ora</a> bookshelf speakers for the higher price of $349. These speakers only come in black, but they offer a wider soundstage and studio sound for music production or mixing. There are also the <a href="https://www.tomsguide.com/audio/kanto-ora4-review">Kanto Ora4</a> for $399, which are the same as the Ora but powered. </p><p>A cheaper way to upgrade your desk setup is the <a href="https://www.tomsguide.com/audio/speakers/jbl-104-bt">JBL 104-BT</a> speakers for $199. They have a wide sound but struggle in the low end. I personally don’t think they look as good as the Kanto speakers, but they do the job just fine. </p><p>If you’re short on space and need to save some space (and money), the <a href="https://www.tomsguide.com/audio/speakers/creative-pebble-pro-review">Creative Pebble Pro </a>speakers cost just $69. The speakers have powerful bass, but I prefer the soundstage of the Kanto UKI speakers. </p><h2 class="article-body__section" id="section-kanto-uki-review-design"><span>Kanto UKI review: Design </span></h2><p>The Kanto UKI speakers are an undeniably stylish duo that has brightened up my desk. I tested the Sage colorway of the speakers, and since my room is decorated with a lot of green furniture, they fit right in. </p><p>Each of the speakers measures 6.5 x 4.4 x 4.3 inches, so they’re very compact. They’re smaller than the <a href="https://www.tomsguide.com/audio/speakers/edifier-r1280t-review">Edifier R1280T</a> speakers ($149), which measure 9.2 x 7.7 x 5.7 inches. If you’re short on space, the Kanto speakers are a no-brainer. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CXXy9yLH4MsajcHDK7zLUC" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/CXXy9yLH4MsajcHDK7zLUC.jpg" 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>They are made from partially recycled hard plastic and feel very sturdy. The front of each speaker has an exposed tweeter and woofer. </p><p>At the back of each speaker, there’s a thread for mounting it to the <a href="https://kantoaudio.com/speaker-accessories/su2/" target="_blank" rel="nofollow">Kanto SU2 speaker stands, which are available for $29</a>. This addition will lift the speakers off the desk, giving you more space and reducing surface reflections, enhancing the quality of your playback. Adding these stands made the listening experience ten times better, and I recommend this addition to anyone who buys these speakers. </p><h3 id="controls-4">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kaKAQEx5iBXcnZ2JvSVfHC" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/kaKAQEx5iBXcnZ2JvSVfHC.jpg" 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 controls on the speakers are very minimal and simplistic, but that makes the experience straightforward. They remind me of the ones I had with my family PC in the 2000s. There's one knob on the front of the main speaker that you press to switch connectivity options and turn to adjust volume. </p><p>At the back of the main speaker, you’ll find the Bluetooth button and a small left/right switch. By toggling this switch, you can switch between the left and right output speakers, meaning you can arrange them however you like on your desk. This made it easy for me to arrange my setup according to where the power outlets in the room are. </p><h3 id="connectivity-4">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="obHgAU7Q2Y8nDJRttHugqC" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/obHgAU7Q2Y8nDJRttHugqC.jpg" 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>At the back of the main speaker, you’ll see all of the ports for the different connectivity options. You can listen to music using Bluetooth 5.3, USB-C or RCA inputs, giving you versatility to use them with computers, phone streaming, or hooked up to a record player. There is also a 3.5mm headphone output port at the front of the speaker. </p><p>The speakers need to be connected to power when in use. There’s a six-foot four-pin speaker connector included in the box to connect the left and right speakers. </p><h2 class="article-body__section" id="section-kanto-uki-review-sound-quality"><span>Kanto UKI review: Sound quality</span></h2><p>I listened using the Kanto UKI speakers connected to my computer via USB-C. I’ve enjoyed listening to my music with the bookshelf speakers every day. Overall, the sound from these speakers is very warm, and the reproduction feels very natural. On their own, they’re perfect for casual listening but lack a wide soundstage; however, when I added the Kanto SU2 Desktop Stands to the setup, the sound was transformed. </p><p>When speakers are placed directly onto a surface like your desk, the sound can be subject to reflection. This means that they begin to sound muffled. But with the addition of speaker stands that can direct the sound upwards, this muffled sound is reduced, and sound reflection diminishes. These stands are a saving grace for these speakers. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CbGGTy7EtwGSTFkSBEu2gC" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/CbGGTy7EtwGSTFkSBEu2gC.jpg" 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 the bass performance, I listened to <em>Nightmare Tripping </em>by Don Broco. This song relies heavily on the deep bassline, but that wasn’t replicated very well here. Some of the depth was lost, with more focus being put on the vocals. But this is to be expected with these speakers as they bottom out at 80Hz and are primarily designed for detailed listening, prioritizing the mids performance. But a deep bass performance is still achievable if that’s what you’re looking for, as you can pair the speakers with a small sub offered by <a href="https://www.amazon.com/Kanto-Powered-Subwoofer-Accurate-Ultra-Compact/dp/B0FGJTV73D" target="_blank" rel="nofollow">Kanto, such as the Sub6 for $299</a>.</p><p>The mid-performance was impressive. I listened to <em>Wheels</em> by the Foo Fighters. This song is country-esque (for the Foos) and features powerful drums and a bright electric guitar. The drums were well defined throughout the song, and the electric guitar also sounded very clean.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="F6sbJftqYFz7T64gec6rAD" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/F6sbJftqYFz7T64gec6rAD.jpg" 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>Finally, I listened to <em>Next To Me</em> by Kingfishr to test the treble and vocal performance. This song is soft and acoustic, lending itself to showcasing the more delicate performance of the speakers. </p><p>The vocals and harmonies throughout the song were clear with good separation. The guitar and harmonica also showed off the bright sound of the speakers and the warmth they can replicate.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9nCAtLKCBqRE62urcLUj6C" name="Kanto UKI" alt="Kanto UKI bookshelf speakers in sage photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/9nCAtLKCBqRE62urcLUj6C.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also used the speakers while watching Invincible, an action TV show, and was impressed by the level of detail and how the sound effects moved between the speakers. </p><h2 class="article-body__section" id="section-kanto-uki-review-verdict"><span>Kanto UKI review: Verdict</span></h2><p>The Kanto UKI bookshelf speakers have been a great addition to my desk setup. I can listen to them all day and know I’ll get an enjoyable, consistent sound. The simple controls make it easy to make quick volume adjustments or switch over connectivity. </p><p>The diverse selection of connectivity options is ideal if you want to use the speakers connected to a PC, wirelessly to your phone, or with a more bespoke setup with a turntable. The small size means they’re easy to store, and the cute design and color options are the cherry on top. </p><p>Unfortunately, these speakers struggle with deep bass tracks, but you could spend a little extra on the Kanto Ora instead if that bothers you. The narrow soundstage also won’t be ideal for anyone looking for desktop speakers to support music production or mixing. With that said, these speakers are ideal for casual, everyday listening while working. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ These $899 headphones compensate for hearing loss — I’ve got tinnitus so I put them to the test ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Noble Audio FoKus Artemis are the latest over-ear ANC headphones from high-end, although slightly niche IEM manufacturer Noble Audio. The Artemis follow up the FoKus Apollo Pro, adding an extra set of drivers to take the tally to three: each side now has a dynamic, a planar magnetic, and a balanced armature driver.</p><p>There’s also a slew of other upgrades, from Bluetooth and codec compatibility to water resistance. Most importantly, they sound utterly, and I mean <em>utterly</em> divine — those triple drivers aided by a clever personalized EQ that compensates for per-ear hearing loss.</p><p>Even so, they’re not perfect. At $899, these cans are competing with some serious players — your Bang & Olufsens; your Bowers & Wilkinses. While premium, the Artemis can’t quite match the luxurious build or polished companion apps of rivals. </p><p>But do the Artemis still rank as some of the <a href="https://www.tomsguide.com/us/best-noise-cancelling-headphones,review-5566.html"><u>best noise-canceling headphones</u></a> for audiophiles? Find out in my full Noble Audio FoKus Artemis review.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-specs"><span>Noble Audio FoKus Artemis review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://nobleaudio.com/products/artemis" target="_blank" rel="nofollow"><u>$899</u></a> / <a href="https://nobleaudio.com/products/artemis" target="_blank" rel="nofollow"><u>£799</u></a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Closed back</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>Triple-driver: balanced armature/planar magnetic/dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>Unspecified</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP52</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth version 6.0; USB-C; 3.5mm</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>55 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>11.4 ounces / 324 grams</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black & silver</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-price-availability"><span>Noble Audio FoKus Artemis 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:1304px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="fnhr7zs3QV3kDwam7NxRNc" name="Noble Audio FoKus Artemis-8" alt="The Noble Audio FoKus Artemis in hand showing the Noble branding" src="https://cdn.mos.cms.futurecdn.net/v2/t:83,l:381,cw:1304,ch:734,q:80/fnhr7zs3QV3kDwam7NxRNc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>As I mentioned above, at <a href="https://nobleaudio.com/products/artemis" target="_blank" rel="nofollow"><u>$899</u></a> / <a href="https://nobleaudio.com/products/artemis" target="_blank" rel="nofollow"><u>£799</u></a>, the Artemis are pricey enough to mix it with the big boys — the <a href="https://www.tomsguide.com/audio/over-ear-headphones/bang-and-olufsen-beoplay-h95-review-headphones"><u>Bang & Olufsen Beoplay H95</u></a> ($1,250 / £879), and 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"><u>Bowers & Wilkins Px8 S2</u></a> ($799 / £675). I own both the H95 and Px8 S2, so I’ve tested all three. </p><p>In short, if all you care about is sound, the FoKus Artemis edge it, with greater audio finesse thanks to the triple-driver array and excellent personalized EQ. If, however, you want a holistic luxury package for your close-to-a-grand, it’d have to be the H95. The Px8 S2 are the cheapest of all three but still feel more luxurious and put-together than the Artemis, plus they sound fantastic.</p><p>In the U.S., the Artemis are in pre-order at Noble Audio as I write this, although they're already becoming available via its established retailers in Britain.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-design"><span>Noble Audio FoKus Artemis review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hZreVuJd6Mwda3hcj4UVNc" name="Noble Audio FoKus Artemis-4" alt="A close up of the Noble Audio FoKus Artemis earcup details, on a stone surface, propped up on a plinth, against a blue background" src="https://cdn.mos.cms.futurecdn.net/hZreVuJd6Mwda3hcj4UVNc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>There’s no denying: the FoKus Artemis are hella pretty headphones. The glorious swirling matte patina on the outer cups is borderline addictive to behold. The sliders and earcup hangers are polished to a semi-matte, with a muted, syrupy luster as the light catches them. The hangers feel reassuringly chunky, I ought to add.</p><p>But while the Artemis look charming and feel premium, they don’t convey <em>luxury</em>, which I’d expect at this price. As I mentioned above, when you charge $899 for headphones, there’s nowhere to hide, and some incredibly fierce competition. Noble is splashing around in Bowers’ and B&O’s paddling pool — and that means it’s a pool constructed entirely of titanium and lambskin leather. </p><p>The Artemis simply cannot match the opulence of the other two, who leave no detail without attention. The Artemis’ milled edges are just that little rougher; the etching slightly less sharp; the ear pads (while comfy) not nearly as pillowy-soft. The buttons look plasticky and cheap, while the legends look either very slightly mis- or double-printed on both my test units — you wouldn’t find a basic error like that on a set of Bowers, <em>no</em> <em>siree</em>, and I’m pretty sure someone at B&O would be facing a firing squad for 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:1236px;"><p class="vanilla-image-block" style="padding-top:56.31%;"><img id="LhanSksuoihK2MydwaxvMc" name="Noble Audio FoKus Artemis-11" alt="The Noble Audio FoKus Artemis in hand showing the control buttons" src="https://cdn.mos.cms.futurecdn.net/v2/t:343,l:325,cw:1236,ch:696,q:80/LhanSksuoihK2MydwaxvMc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>There’s a felt-like material on the back of the Artemis earcups, which unless cossetted will doubtless grubby up quicker than you can say ‘balanced armature’. And the headband fabric looks uncannily like that on the <a href="https://www.tomsguide.com/computing/peripherals/jabra-evolve3-75-review"><u>Jabra Evolve3 75</u></a> conferencing headset I tested recently — there’s nothing overtly <em>wrong</em> with it, and it’s perfectly comfortable… but $900 to look like you’re on a work call?</p><p>By $500 headphone standards, the Artemis sure are lovely. But Noble has chosen the $900 mark, so by that yardstick shall these cans be measured, and shall they ever-so-slightly disappoint.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2nA5jz6BP9dhREYyw4J5Pc" name="Noble Audio FoKus Artemis-12" alt="The Noble Audio FoKus Artemis earcups removed and in hand" src="https://cdn.mos.cms.futurecdn.net/2nA5jz6BP9dhREYyw4J5Pc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Still, the Artemis boast some worthy design features. The earcups and battery are replaceable, theoretically benefitting longevity. IP52 dust-/water-resistance means they’ll survive a light shower and the odd workout — although I dread to think what that earcup felt will look (not to mention smell) like once sweated into. You can replace the earcups on both the Px8 S2 and H95, but only the latter is water resistant (also IP52) and neither can have their batteries replaced (not by <em>you</em> 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:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="tRrzqbh9wU28m6To2vE2rb" name="Noble Audio FoKus Artemis_Test-1" alt="A selfie photo of Editor Peter Wolinski wearing a set of the Noble Audio FoKus Artemis on a runway as he is about to board a plane." src="https://cdn.mos.cms.futurecdn.net/tRrzqbh9wU28m6To2vE2rb.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">I took the Artemis with me on a press trip to Copenhagen! </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Artemis come with a hardened fabric carry case. It’s a little slimmer than the Px8 S2’s equivalent and wholly easier to travel with than the H95’s chunky, scratch-magnetic hard-shell case. I’ve travelled with all three and prefer the Artemis’ case, as it took up the least space in my bag.</p><h3 id="controls-5">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qxkHnUdpJyUYiavxH6WpJc" name="Noble Audio FoKus Artemis-10" alt="The Noble Audio FoKus Artemis in hand showing the ANC button" src="https://cdn.mos.cms.futurecdn.net/qxkHnUdpJyUYiavxH6WpJc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Their cheap-ish looks aside, I’m a big fan of the Artemis’ buttons and controls. They’re reliable, unfussy and sensibly positioned, which is all I really want and need. I had no issues locating and operating the Artemis’s controls by touch, even while walking around Copenhagen on a press trip. Sure, I like the physical and touch controls on my <a href="https://www.tomsguide.com/audio/headphones/bang-and-olufsen-beoplay-h100-review"><u>Bang & Olufsen Beoplay H100</u></a> and H95, but how many do I <em>really</em> use day-to-day? Four: power, volume, playback and ANC. </p><h3 id="connectivity-5">Connectivity</h3><p>The Artemis use Bluetooth version 6.0, providing greater connection stability and lower power consumption than earlier versions. Even the <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review"><u>Apple AirPods Max 2</u></a> launched in 2026 with the older version 5.3, so (unfortunately) the latest iteration isn't something you can take for granted.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1348px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="X8FeXUnZzEsGJj94cGFRkb" name="Noble Audio FoKus Artemis-6" alt="The Noble Audio FoKus Artemis in hand showing the USB-C port" src="https://cdn.mos.cms.futurecdn.net/v2/t:292,l:362,cw:1348,ch:758,q:80/X8FeXUnZzEsGJj94cGFRkb.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Artemis support hi-res codecs, including LDAC, aptX HD, and now aptX Lossless — an upgrade from the Apollo. Again, you can’t take these for granted, as the $2,500 B&O H100 only support basic AAC/SBC codecs, meaning no hi-res wireless.</p><p>The Artemis also pack wear detection with customizable auto-off timing, multi-point connectivity and smartphone control via a (rather janky) companion app. You can also run the cans truly lossless via USB-C, and there’s a USB-C 3.5mm adapter included. </p><p>Annoyingly, multipoint didn’t work too well during testing — when taking work calls, I occasionally had to turn the Artemis off and on again for Google Meet to recognize them as an audio/mic source. Could be a Meet issue, though.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-sound-quality"><span>Noble Audio FoKus Artemis review: Sound quality</span></h2><p>I tested over Bluetooth using AAC, and over USB-C in hi-res on <a href="https://www.tomsguide.com/audio/qobuz-review"><u>Qobuz</u></a>. As I mentioned above, the Artemis sound insanely good. The three-driver setup comprises a dynamic driver for bass and mid-range impact; a planar magnetic driver for super fast response, particularly in the mid- and upper ranges; and a balanced armature driver for fine treble detail. It’s a heavenly <em>ménage à trois</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="ThpQmvyYuiMUMQrCFTV5Pc" name="Noble Audio FoKus Artemis-14" alt="The Noble Audio FoKus Artemis with their earcups removed to show the drivers" src="https://cdn.mos.cms.futurecdn.net/ThpQmvyYuiMUMQrCFTV5Pc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Treble is ultra-precise, airy, and rarely fatiguing — I was caught out a couple of times by songs with inherently sharp treble baked into their master, although admittedly I <em>was</em> hungover on both occasions. For the most part, though, I found the treble comfortable enough for hours on end. </p><p>The balanced armature (BA) drivers and ultra-responsive planars offer exceptional detail. In Blusher’s <em>Your Love Is My Drug,</em> I heard every minute enunciation, every mouth noise, every lip smack, and the gentlest whisps of the breathy vocals during harmonies. The balanced armature drivers also help with airiness and space — the subtle pop of interference at the start of Queens of the Stone Age’s <em>Medication</em> was imaged perfectly, crackling upward and forward in my left ear like a firing nerve ending.</p><p>Vocals steal the show, though. They sound mesmerizing across the board, always perfectly isolated from percussion and other mid-range noise. In Stereophonics’ <em>Just Looking</em>, Kelly Jones’ voice commanded the track, his husky vocals floating around my head like tobacco smoke in an 80s dive bar — I almost forgot the song had a riff and drum track.</p><p>That fast mid-range response also works wonders elsewhere, with strong instrument and tonal separation. Guitars sound fantastic. I could hear the individual plucks and natural stringy timbre during the acoustic guitar bridge of Porter Robinson’s <em>Cheerleader</em>. Distorted stuff works, too, with bags of texture and definition in sludgy metal riffs.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bKAN5KhnYt8ArPXt8e6cPc" name="Noble Audio FoKus Artemis-16" alt="The Noble Audio FoKus Artemis carry case, open showing the cable tidy compartment, against a blue background" src="https://cdn.mos.cms.futurecdn.net/bKAN5KhnYt8ArPXt8e6cPc.jpg" 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 Artemis come with USB-C and 3.5mm cables, plus adapters, for lossless wired playback. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Artemis aren’t overly bassy headphones — no <a href="https://www.tomsguide.com/audio/over-ear-headphones/bose-quietcomfort-ultra-headphones-2nd-generation-review"><u>Bose QuietComfort Ultra Gen 2</u></a> tuning for the masses. But when you need the bass, it’s there, impactful and well-controlled. The powerful warbles of Burn Water’s <em>Ikigai</em> were thick, rich and velvety-textured.</p><p>Sub-bass response isn’t the strongest, at least not on my personal EQ. The last 20 seconds of Sohn's <em>Antigravity</em> — essentially just a wave of the deepest sub bass you can get before feeling rather than hearing it — sounded fairly thin. But that stuff is tricky for pretty much all headphones I’ve tested save the $2,200 Beoplay H100 and $4,500 Audeze LCD5-S. </p><p>As I mentioned above, these headphones are surprisingly spacious for closed-backs, but there’s an extra mode to supposedly increase three-dimensionality: Audiosphere, available in either Subtle (weak) or Immersive (strong) settings. Like most software-based 3D modes I try, it’s pretty poor, making everything sound metallic and echoey rather than actually increasing spatiality.</p><p><em>Listen along to my audio testing playlist on </em><a href="https://www.tomsguide.com/audio/qobuz-review"><u><em>Qobuz</em></u></a><em>, our favourite </em><a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"><u><em>music streaming service</em></u></a><em> for audiophiles.</em></p><iframe allow="" height="390" width="400" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://widget.qobuz.com/playlist/59666746?zone=GB-en"></iframe><h3 id="personalized-eq">Personalized EQ</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:60.00%;"><img id="8HYjc9JwtePJe3NCRY4jda" name="Noble Audio FoKus Artemis_App-1" alt="Two screenshots of the Noble Audio FoKus mobile app being used to set a custom audiodo EQ for the FoKus Artemis" src="https://cdn.mos.cms.futurecdn.net/8HYjc9JwtePJe3NCRY4jda.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1536" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/8HYjc9JwtePJe3NCRY4jda.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Noble Audio / Future)</span></figcaption></figure><p>The Artemis’ standout software feature is undoubtedly its personalized EQ, courtesy of the audio algo-nerds at Audiodo, a Swedish company specializing in personal EQing. The personalized EQ runs a hearing test and then adapts the EQ profile to your individual ears, compensating for any hearing loss at different frequencies in one ear or the other. As a tinnitus sufferer, motorcyclist and former DJ, this is exactly what I need!</p><p>I was blown away by the results. My personal EQ was highly effective, brightening up the treble and bringing noticeable space to the sound without compromising anything else.</p><p>There’s also a standard 10-band EQ, plus various presets. I like that your EQ is also saved onboard the headphones, too, so if your app goes kaput (which, spoiler alert, it probably will), your chosen EQ <em><strong>should</strong></em> remain on the cans... and yes, that is some not-so-subtle foreshadowing.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-app"><span>Noble Audio FoKus Artemis review: App</span></h2><p>The Artemis’ Achilles heel (staying on the mythical trend) is its app. It’s a little unpolished, with a few slightly awkward <em>something</em>-to-English direct translations (“Go test” and the “Setting” menu), plus a somewhat boxy, dated and disconcertingly left-justified interface.</p><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="e2RqkbHJry67dP6AANWHna" name="Noble Audio FoKus Artemis_App-2" alt="A screenshot of the Noble Audio FoKus mobile app showing a set of open earbuds when it should show the FoKus Artemis." src="https://cdn.mos.cms.futurecdn.net/e2RqkbHJry67dP6AANWHna.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1440" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/e2RqkbHJry67dP6AANWHna.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">...unkonw? </span><span class="credit" itemprop="copyrightHolder">(Image credit: Noble Audio / Future)</span></figcaption></figure><p>However, a more concerning issue during testing was the app’s jankiness. It sometimes failed to recognize the headphones at all, and once thought they were Noble’s Karma open earbuds. It also crashed each time I tried to update the Artemis — not ideal given my first test set was running early firmware. </p><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="v76QhrNHLeRHyQiieVd9Aa" name="Noble Audio FoKus Artemis_App-3" alt="Two screenshots of the Noble Audio FoKus mobile app showing two sets of different custom EQs." src="https://cdn.mos.cms.futurecdn.net/v76QhrNHLeRHyQiieVd9Aa.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1440" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/v76QhrNHLeRHyQiieVd9Aa.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Before: My personalized EQs before the firmware update. As you can see in the green boxes and from the EQ curves, I have two separate EQ profiles. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Noble Audio / Future)</span></figcaption></figure><p>I flagged this to Noble’s representative and the manufacturer quickly patched the iOS app, which is fine, although after the update one of my two personal EQ profiles was overwritten by a duplication of the other (thankfully, the one I preferred). It’s not exactly confidence-inspiring. </p><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="Qm5yzM734pHGQXm5bGn6Ya" name="Noble Audio FoKus Artemis_App-4" alt="Two screenshots of the Noble Audio FoKus mobile app showing two sets of different custom EQs." src="https://cdn.mos.cms.futurecdn.net/Qm5yzM734pHGQXm5bGn6Ya.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1440" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/Qm5yzM734pHGQXm5bGn6Ya.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">After: And here are my personalized EQ profiles after I (eventually) updated the firmware. As you can see from the green boxes and EQ curves, my two profiles are now the same, meaning one was overwritten with a dupe. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Noble Audio / Future)</span></figcaption></figure><p>At $899 and in 2026, I’d expect a little more polish given the smartphone app is so pivotal to the Artemis’ user experience — personalized EQ and updating <em>et al</em>. The B&O app, by contrast, is wonderful and feature-packed, while the B&W app is fairly basic but at least reliable and slick. </p><p>I should mention that, thankfully, despite the jankiness, I could still use the FoKus app fairly reliably to customize the Artemis at a fundamental level (controls, ANC levels, etc), and most importantly use the EQ. Given the EQ is saved onboard the headphones, after setting that you can mostly live without the app anyway until you need to tweak EQ or update the cans.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-anc"><span>Noble Audio FoKus Artemis review: 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:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rcp2AnwDhkYzfhrJSGMiDb" name="Noble Audio FoKus Artemis_Test-2" alt="A selfie photo of Editor Peter Wolinski wearing a set of the Noble Audio FoKus Artemis onboard a plane." src="https://cdn.mos.cms.futurecdn.net/rcp2AnwDhkYzfhrJSGMiDb.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">The Artemis' ANC faced a stern test on my flight home. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The FoKus Artemis’ ANC is pretty good. I took the headphones on a flight to Copenhagen, and they cut out enough hubbub in the busy departures terminal for me to get on with some work in peace. On the plane, they competently drowned out the noisy Airbus A320 I flew on, the people behind me talking, plus a passenger on my row snoring. They couldn’t cut the noise of a screaming kid behind me on the return flight though, which the Bowers & Wilkins Px8 S2 did <a href="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"><u>last time I flew</u></a> (returning, coincidentally, from Copenhagen).</p><p>The Artemis did a respectable job in daily life, too, cutting most traffic noise while walking around the Danish capital. I didn’t get along with the pressure levels at times, as the ‘High’ ANC setting occasionally made me feel nauseous, but your mileage will vary based on individual ear anatomy.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-call-quality"><span>Noble Audio FoKus Artemis review: 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:1660px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="qkx54VX952ZtimzkHfYyNc" name="Noble Audio FoKus Artemis-7" alt="The Noble Audio FoKus Artemis in hand showing the controls" src="https://cdn.mos.cms.futurecdn.net/v2/t:103,l:0,cw:1660,ch:934,q:80/qkx54VX952ZtimzkHfYyNc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>For the Artemis, Noble did away with the Apollo’s detachable boom mic, which is a good thing. This is a set of ANC headphones, not a <a href="https://www.tomsguide.com/best-picks/best-gaming-headsets"><u>gaming headset</u></a>. The new internal mic does a fairly good job for calls. I called my wife, who reported my voice sounded clear and natural, if not a little bit thin. </p><p>She noted, however, that the Artemis mic picked up literally every single background noise, from rustling paper to unwrapping a parcel to merely pulling my keys from my pocket. Apparently this was very distracting, and with no environmental noise cancelation on the mic, there was nothing I could do about it.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-battery-life"><span>Noble Audio FoKus Artemis review: Battery life</span></h2><p>Noble promises 55 hours of battery life with ANC off and 35 with ANC on. This sits between the 38-hour and 30-hour ANC figures of the H95 and Px8 S2 respectively. It’s a lot better than the 20 hours you can expect from the AirPods Max 2.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1492px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="nTpVuqabnbxqeZNuxEyFQc" name="Noble Audio FoKus Artemis-13" alt="The Noble Audio FoKus Artemis with their earcups removed to show the drivers and removable battery" src="https://cdn.mos.cms.futurecdn.net/v2/t:284,l:271,cw:1492,ch:840,q:80/nTpVuqabnbxqeZNuxEyFQc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Real world results will of course vary based on your individual usage and whether you’re using USB-C for playback. In real terms, though, I used the Artemis extensively over a three-day press trip without charging them and was left with around 50% left, so there’s more than enough juice for a short trip away, and you’ll very likely be able to find a power socket in that time anyway.</p><p>The Artemis also have a neat trick up their sleeve: take off the left ear pad and you’ll see a battery held in place with a screw! That means you can replace the battery yourself, theoretically increasing the Artemis’ longevity. We’ve seen this before on the <a href="https://www.tomsguide.com/audio/headphones/sennheiser-momentum-5-review"><u>Sennheiser Momentum 5</u></a>, but it’s not commonplace. Of course, it relies on Noble still making these batteries whenever in the future you need to replace yours. Despite questionable upsides, though, there are certainly no downsides to this.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-verdict"><span>Noble Audio FoKus Artemis review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Vbxw55cC3N8m8tKJ8RQNCc" name="Noble Audio FoKus Artemis-5" alt="The Noble Audio FoKus Artemis on a stone surface, propped up on a plinth, against a blue background" src="https://cdn.mos.cms.futurecdn.net/Vbxw55cC3N8m8tKJ8RQNCc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The FoKus Artemis aren’t perfect. When you’re being charged $899, you deserve a certain level of polish that, unfortunately, isn’t quite there in places. Admittedly, most of this is fairly minor, like the Artemis’ overly sensitive mic, or their lacking the sheer opulent finish and attention to detail of similarly priced rivals. The amateurism of the companion app is more worrying, though, given its importance to the headphones’ core operation. Thankfully that’s something which can be improved over time (and really needs to be, Noble, if you’re reading).</p><p>That said, I’m still head over heels in love with the FoKus Artemis, and there’s no question that they deserve my recommendation. Because <em>that</em> <em>sound</em> — mah gawd. It's what these cans are all about. Fire them up, sort out your personalized EQ, and you’ll quickly realize what you’ve paid for. The sound is simply exquisite, with detail, space, power in abundance, yet balance. The $900 finesse lacked elsewhere is made up for in triumphant fashion by just listening to the things. And that’s a compromise I can live with.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/over-ear-headphones/noble-audio-fokus-artemis-review</link>
                                                                            <description>
                            <![CDATA[ The luxury price tag is justified by incredible sound and a clever personal EQ. While not perfect, the Noble Audio FoKus Artemis are still utterly beautiful. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">duTuemro67gffvMvKqZoDK</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/JymsYP53jrKRHhMxGNq5fb-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 04 Sep 2026 11:44:06 +0000</pubDate>                                                                                                                                <updated>Fri, 04 Sep 2026 12:26:05 +0000</updated>
                                                                                                                                            <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.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/JymsYP53jrKRHhMxGNq5fb-1280-80.jpg">
                                                            <media:credit><![CDATA[Peter Wolinski / Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Noble Audio FoKus Artemis on a stone surface, propped up on a plinth, against a blue background]]></media:description>                                                            <media:text><![CDATA[The Noble Audio FoKus Artemis on a stone surface, propped up on a plinth, against a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[The Noble Audio FoKus Artemis on a stone surface, propped up on a plinth, against a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/JymsYP53jrKRHhMxGNq5fb-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Noble Audio FoKus Artemis are the latest over-ear ANC headphones from high-end, although slightly niche IEM manufacturer Noble Audio. The Artemis follow up the FoKus Apollo Pro, adding an extra set of drivers to take the tally to three: each side now has a dynamic, a planar magnetic, and a balanced armature driver.</p><p>There’s also a slew of other upgrades, from Bluetooth and codec compatibility to water resistance. Most importantly, they sound utterly, and I mean <em>utterly</em> divine — those triple drivers aided by a clever personalized EQ that compensates for per-ear hearing loss.</p><p>Even so, they’re not perfect. At $899, these cans are competing with some serious players — your Bang & Olufsens; your Bowers & Wilkinses. While premium, the Artemis can’t quite match the luxurious build or polished companion apps of rivals. </p><p>But do the Artemis still rank as some of the <a href="https://www.tomsguide.com/us/best-noise-cancelling-headphones,review-5566.html"><u>best noise-canceling headphones</u></a> for audiophiles? Find out in my full Noble Audio FoKus Artemis review.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-specs"><span>Noble Audio FoKus Artemis review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://nobleaudio.com/products/artemis" target="_blank" rel="nofollow"><u>$899</u></a> / <a href="https://nobleaudio.com/products/artemis" target="_blank" rel="nofollow"><u>£799</u></a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Closed back</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>Triple-driver: balanced armature/planar magnetic/dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>Unspecified</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP52</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth version 6.0; USB-C; 3.5mm</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>55 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>11.4 ounces / 324 grams</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black & silver</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-price-availability"><span>Noble Audio FoKus Artemis 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:1304px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="fnhr7zs3QV3kDwam7NxRNc" name="Noble Audio FoKus Artemis-8" alt="The Noble Audio FoKus Artemis in hand showing the Noble branding" src="https://cdn.mos.cms.futurecdn.net/v2/t:83,l:381,cw:1304,ch:734,q:80/fnhr7zs3QV3kDwam7NxRNc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>As I mentioned above, at <a href="https://nobleaudio.com/products/artemis" target="_blank" rel="nofollow"><u>$899</u></a> / <a href="https://nobleaudio.com/products/artemis" target="_blank" rel="nofollow"><u>£799</u></a>, the Artemis are pricey enough to mix it with the big boys — the <a href="https://www.tomsguide.com/audio/over-ear-headphones/bang-and-olufsen-beoplay-h95-review-headphones"><u>Bang & Olufsen Beoplay H95</u></a> ($1,250 / £879), and 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"><u>Bowers & Wilkins Px8 S2</u></a> ($799 / £675). I own both the H95 and Px8 S2, so I’ve tested all three. </p><p>In short, if all you care about is sound, the FoKus Artemis edge it, with greater audio finesse thanks to the triple-driver array and excellent personalized EQ. If, however, you want a holistic luxury package for your close-to-a-grand, it’d have to be the H95. The Px8 S2 are the cheapest of all three but still feel more luxurious and put-together than the Artemis, plus they sound fantastic.</p><p>In the U.S., the Artemis are in pre-order at Noble Audio as I write this, although they're already becoming available via its established retailers in Britain.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-design"><span>Noble Audio FoKus Artemis review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hZreVuJd6Mwda3hcj4UVNc" name="Noble Audio FoKus Artemis-4" alt="A close up of the Noble Audio FoKus Artemis earcup details, on a stone surface, propped up on a plinth, against a blue background" src="https://cdn.mos.cms.futurecdn.net/hZreVuJd6Mwda3hcj4UVNc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>There’s no denying: the FoKus Artemis are hella pretty headphones. The glorious swirling matte patina on the outer cups is borderline addictive to behold. The sliders and earcup hangers are polished to a semi-matte, with a muted, syrupy luster as the light catches them. The hangers feel reassuringly chunky, I ought to add.</p><p>But while the Artemis look charming and feel premium, they don’t convey <em>luxury</em>, which I’d expect at this price. As I mentioned above, when you charge $899 for headphones, there’s nowhere to hide, and some incredibly fierce competition. Noble is splashing around in Bowers’ and B&O’s paddling pool — and that means it’s a pool constructed entirely of titanium and lambskin leather. </p><p>The Artemis simply cannot match the opulence of the other two, who leave no detail without attention. The Artemis’ milled edges are just that little rougher; the etching slightly less sharp; the ear pads (while comfy) not nearly as pillowy-soft. The buttons look plasticky and cheap, while the legends look either very slightly mis- or double-printed on both my test units — you wouldn’t find a basic error like that on a set of Bowers, <em>no</em> <em>siree</em>, and I’m pretty sure someone at B&O would be facing a firing squad for 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:1236px;"><p class="vanilla-image-block" style="padding-top:56.31%;"><img id="LhanSksuoihK2MydwaxvMc" name="Noble Audio FoKus Artemis-11" alt="The Noble Audio FoKus Artemis in hand showing the control buttons" src="https://cdn.mos.cms.futurecdn.net/v2/t:343,l:325,cw:1236,ch:696,q:80/LhanSksuoihK2MydwaxvMc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>There’s a felt-like material on the back of the Artemis earcups, which unless cossetted will doubtless grubby up quicker than you can say ‘balanced armature’. And the headband fabric looks uncannily like that on the <a href="https://www.tomsguide.com/computing/peripherals/jabra-evolve3-75-review"><u>Jabra Evolve3 75</u></a> conferencing headset I tested recently — there’s nothing overtly <em>wrong</em> with it, and it’s perfectly comfortable… but $900 to look like you’re on a work call?</p><p>By $500 headphone standards, the Artemis sure are lovely. But Noble has chosen the $900 mark, so by that yardstick shall these cans be measured, and shall they ever-so-slightly disappoint.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2nA5jz6BP9dhREYyw4J5Pc" name="Noble Audio FoKus Artemis-12" alt="The Noble Audio FoKus Artemis earcups removed and in hand" src="https://cdn.mos.cms.futurecdn.net/2nA5jz6BP9dhREYyw4J5Pc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Still, the Artemis boast some worthy design features. The earcups and battery are replaceable, theoretically benefitting longevity. IP52 dust-/water-resistance means they’ll survive a light shower and the odd workout — although I dread to think what that earcup felt will look (not to mention smell) like once sweated into. You can replace the earcups on both the Px8 S2 and H95, but only the latter is water resistant (also IP52) and neither can have their batteries replaced (not by <em>you</em> 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:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="tRrzqbh9wU28m6To2vE2rb" name="Noble Audio FoKus Artemis_Test-1" alt="A selfie photo of Editor Peter Wolinski wearing a set of the Noble Audio FoKus Artemis on a runway as he is about to board a plane." src="https://cdn.mos.cms.futurecdn.net/tRrzqbh9wU28m6To2vE2rb.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">I took the Artemis with me on a press trip to Copenhagen! </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Artemis come with a hardened fabric carry case. It’s a little slimmer than the Px8 S2’s equivalent and wholly easier to travel with than the H95’s chunky, scratch-magnetic hard-shell case. I’ve travelled with all three and prefer the Artemis’ case, as it took up the least space in my bag.</p><h3 id="controls-5">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qxkHnUdpJyUYiavxH6WpJc" name="Noble Audio FoKus Artemis-10" alt="The Noble Audio FoKus Artemis in hand showing the ANC button" src="https://cdn.mos.cms.futurecdn.net/qxkHnUdpJyUYiavxH6WpJc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Their cheap-ish looks aside, I’m a big fan of the Artemis’ buttons and controls. They’re reliable, unfussy and sensibly positioned, which is all I really want and need. I had no issues locating and operating the Artemis’s controls by touch, even while walking around Copenhagen on a press trip. Sure, I like the physical and touch controls on my <a href="https://www.tomsguide.com/audio/headphones/bang-and-olufsen-beoplay-h100-review"><u>Bang & Olufsen Beoplay H100</u></a> and H95, but how many do I <em>really</em> use day-to-day? Four: power, volume, playback and ANC. </p><h3 id="connectivity-5">Connectivity</h3><p>The Artemis use Bluetooth version 6.0, providing greater connection stability and lower power consumption than earlier versions. Even the <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review"><u>Apple AirPods Max 2</u></a> launched in 2026 with the older version 5.3, so (unfortunately) the latest iteration isn't something you can take for granted.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1348px;"><p class="vanilla-image-block" style="padding-top:56.23%;"><img id="X8FeXUnZzEsGJj94cGFRkb" name="Noble Audio FoKus Artemis-6" alt="The Noble Audio FoKus Artemis in hand showing the USB-C port" src="https://cdn.mos.cms.futurecdn.net/v2/t:292,l:362,cw:1348,ch:758,q:80/X8FeXUnZzEsGJj94cGFRkb.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Artemis support hi-res codecs, including LDAC, aptX HD, and now aptX Lossless — an upgrade from the Apollo. Again, you can’t take these for granted, as the $2,500 B&O H100 only support basic AAC/SBC codecs, meaning no hi-res wireless.</p><p>The Artemis also pack wear detection with customizable auto-off timing, multi-point connectivity and smartphone control via a (rather janky) companion app. You can also run the cans truly lossless via USB-C, and there’s a USB-C 3.5mm adapter included. </p><p>Annoyingly, multipoint didn’t work too well during testing — when taking work calls, I occasionally had to turn the Artemis off and on again for Google Meet to recognize them as an audio/mic source. Could be a Meet issue, though.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-sound-quality"><span>Noble Audio FoKus Artemis review: Sound quality</span></h2><p>I tested over Bluetooth using AAC, and over USB-C in hi-res on <a href="https://www.tomsguide.com/audio/qobuz-review"><u>Qobuz</u></a>. As I mentioned above, the Artemis sound insanely good. The three-driver setup comprises a dynamic driver for bass and mid-range impact; a planar magnetic driver for super fast response, particularly in the mid- and upper ranges; and a balanced armature driver for fine treble detail. It’s a heavenly <em>ménage à trois</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="ThpQmvyYuiMUMQrCFTV5Pc" name="Noble Audio FoKus Artemis-14" alt="The Noble Audio FoKus Artemis with their earcups removed to show the drivers" src="https://cdn.mos.cms.futurecdn.net/ThpQmvyYuiMUMQrCFTV5Pc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Treble is ultra-precise, airy, and rarely fatiguing — I was caught out a couple of times by songs with inherently sharp treble baked into their master, although admittedly I <em>was</em> hungover on both occasions. For the most part, though, I found the treble comfortable enough for hours on end. </p><p>The balanced armature (BA) drivers and ultra-responsive planars offer exceptional detail. In Blusher’s <em>Your Love Is My Drug,</em> I heard every minute enunciation, every mouth noise, every lip smack, and the gentlest whisps of the breathy vocals during harmonies. The balanced armature drivers also help with airiness and space — the subtle pop of interference at the start of Queens of the Stone Age’s <em>Medication</em> was imaged perfectly, crackling upward and forward in my left ear like a firing nerve ending.</p><p>Vocals steal the show, though. They sound mesmerizing across the board, always perfectly isolated from percussion and other mid-range noise. In Stereophonics’ <em>Just Looking</em>, Kelly Jones’ voice commanded the track, his husky vocals floating around my head like tobacco smoke in an 80s dive bar — I almost forgot the song had a riff and drum track.</p><p>That fast mid-range response also works wonders elsewhere, with strong instrument and tonal separation. Guitars sound fantastic. I could hear the individual plucks and natural stringy timbre during the acoustic guitar bridge of Porter Robinson’s <em>Cheerleader</em>. Distorted stuff works, too, with bags of texture and definition in sludgy metal riffs.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bKAN5KhnYt8ArPXt8e6cPc" name="Noble Audio FoKus Artemis-16" alt="The Noble Audio FoKus Artemis carry case, open showing the cable tidy compartment, against a blue background" src="https://cdn.mos.cms.futurecdn.net/bKAN5KhnYt8ArPXt8e6cPc.jpg" 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 Artemis come with USB-C and 3.5mm cables, plus adapters, for lossless wired playback. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The Artemis aren’t overly bassy headphones — no <a href="https://www.tomsguide.com/audio/over-ear-headphones/bose-quietcomfort-ultra-headphones-2nd-generation-review"><u>Bose QuietComfort Ultra Gen 2</u></a> tuning for the masses. But when you need the bass, it’s there, impactful and well-controlled. The powerful warbles of Burn Water’s <em>Ikigai</em> were thick, rich and velvety-textured.</p><p>Sub-bass response isn’t the strongest, at least not on my personal EQ. The last 20 seconds of Sohn's <em>Antigravity</em> — essentially just a wave of the deepest sub bass you can get before feeling rather than hearing it — sounded fairly thin. But that stuff is tricky for pretty much all headphones I’ve tested save the $2,200 Beoplay H100 and $4,500 Audeze LCD5-S. </p><p>As I mentioned above, these headphones are surprisingly spacious for closed-backs, but there’s an extra mode to supposedly increase three-dimensionality: Audiosphere, available in either Subtle (weak) or Immersive (strong) settings. Like most software-based 3D modes I try, it’s pretty poor, making everything sound metallic and echoey rather than actually increasing spatiality.</p><p><em>Listen along to my audio testing playlist on </em><a href="https://www.tomsguide.com/audio/qobuz-review"><u><em>Qobuz</em></u></a><em>, our favourite </em><a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"><u><em>music streaming service</em></u></a><em> for audiophiles.</em></p><iframe allow="" height="390" width="400" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://widget.qobuz.com/playlist/59666746?zone=GB-en"></iframe><h3 id="personalized-eq">Personalized EQ</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:60.00%;"><img id="8HYjc9JwtePJe3NCRY4jda" name="Noble Audio FoKus Artemis_App-1" alt="Two screenshots of the Noble Audio FoKus mobile app being used to set a custom audiodo EQ for the FoKus Artemis" src="https://cdn.mos.cms.futurecdn.net/8HYjc9JwtePJe3NCRY4jda.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1536" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/8HYjc9JwtePJe3NCRY4jda.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Noble Audio / Future)</span></figcaption></figure><p>The Artemis’ standout software feature is undoubtedly its personalized EQ, courtesy of the audio algo-nerds at Audiodo, a Swedish company specializing in personal EQing. The personalized EQ runs a hearing test and then adapts the EQ profile to your individual ears, compensating for any hearing loss at different frequencies in one ear or the other. As a tinnitus sufferer, motorcyclist and former DJ, this is exactly what I need!</p><p>I was blown away by the results. My personal EQ was highly effective, brightening up the treble and bringing noticeable space to the sound without compromising anything else.</p><p>There’s also a standard 10-band EQ, plus various presets. I like that your EQ is also saved onboard the headphones, too, so if your app goes kaput (which, spoiler alert, it probably will), your chosen EQ <em><strong>should</strong></em> remain on the cans... and yes, that is some not-so-subtle foreshadowing.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-app"><span>Noble Audio FoKus Artemis review: App</span></h2><p>The Artemis’ Achilles heel (staying on the mythical trend) is its app. It’s a little unpolished, with a few slightly awkward <em>something</em>-to-English direct translations (“Go test” and the “Setting” menu), plus a somewhat boxy, dated and disconcertingly left-justified interface.</p><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="e2RqkbHJry67dP6AANWHna" name="Noble Audio FoKus Artemis_App-2" alt="A screenshot of the Noble Audio FoKus mobile app showing a set of open earbuds when it should show the FoKus Artemis." src="https://cdn.mos.cms.futurecdn.net/e2RqkbHJry67dP6AANWHna.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1440" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/e2RqkbHJry67dP6AANWHna.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">...unkonw? </span><span class="credit" itemprop="copyrightHolder">(Image credit: Noble Audio / Future)</span></figcaption></figure><p>However, a more concerning issue during testing was the app’s jankiness. It sometimes failed to recognize the headphones at all, and once thought they were Noble’s Karma open earbuds. It also crashed each time I tried to update the Artemis — not ideal given my first test set was running early firmware. </p><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="v76QhrNHLeRHyQiieVd9Aa" name="Noble Audio FoKus Artemis_App-3" alt="Two screenshots of the Noble Audio FoKus mobile app showing two sets of different custom EQs." src="https://cdn.mos.cms.futurecdn.net/v76QhrNHLeRHyQiieVd9Aa.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1440" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/v76QhrNHLeRHyQiieVd9Aa.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Before: My personalized EQs before the firmware update. As you can see in the green boxes and from the EQ curves, I have two separate EQ profiles. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Noble Audio / Future)</span></figcaption></figure><p>I flagged this to Noble’s representative and the manufacturer quickly patched the iOS app, which is fine, although after the update one of my two personal EQ profiles was overwritten by a duplication of the other (thankfully, the one I preferred). It’s not exactly confidence-inspiring. </p><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="Qm5yzM734pHGQXm5bGn6Ya" name="Noble Audio FoKus Artemis_App-4" alt="Two screenshots of the Noble Audio FoKus mobile app showing two sets of different custom EQs." src="https://cdn.mos.cms.futurecdn.net/Qm5yzM734pHGQXm5bGn6Ya.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1440" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/Qm5yzM734pHGQXm5bGn6Ya.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">After: And here are my personalized EQ profiles after I (eventually) updated the firmware. As you can see from the green boxes and EQ curves, my two profiles are now the same, meaning one was overwritten with a dupe. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Noble Audio / Future)</span></figcaption></figure><p>At $899 and in 2026, I’d expect a little more polish given the smartphone app is so pivotal to the Artemis’ user experience — personalized EQ and updating <em>et al</em>. The B&O app, by contrast, is wonderful and feature-packed, while the B&W app is fairly basic but at least reliable and slick. </p><p>I should mention that, thankfully, despite the jankiness, I could still use the FoKus app fairly reliably to customize the Artemis at a fundamental level (controls, ANC levels, etc), and most importantly use the EQ. Given the EQ is saved onboard the headphones, after setting that you can mostly live without the app anyway until you need to tweak EQ or update the cans.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-anc"><span>Noble Audio FoKus Artemis review: 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:2560px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rcp2AnwDhkYzfhrJSGMiDb" name="Noble Audio FoKus Artemis_Test-2" alt="A selfie photo of Editor Peter Wolinski wearing a set of the Noble Audio FoKus Artemis onboard a plane." src="https://cdn.mos.cms.futurecdn.net/rcp2AnwDhkYzfhrJSGMiDb.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">The Artemis' ANC faced a stern test on my flight home. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The FoKus Artemis’ ANC is pretty good. I took the headphones on a flight to Copenhagen, and they cut out enough hubbub in the busy departures terminal for me to get on with some work in peace. On the plane, they competently drowned out the noisy Airbus A320 I flew on, the people behind me talking, plus a passenger on my row snoring. They couldn’t cut the noise of a screaming kid behind me on the return flight though, which the Bowers & Wilkins Px8 S2 did <a href="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"><u>last time I flew</u></a> (returning, coincidentally, from Copenhagen).</p><p>The Artemis did a respectable job in daily life, too, cutting most traffic noise while walking around the Danish capital. I didn’t get along with the pressure levels at times, as the ‘High’ ANC setting occasionally made me feel nauseous, but your mileage will vary based on individual ear anatomy.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-call-quality"><span>Noble Audio FoKus Artemis review: 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:1660px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="qkx54VX952ZtimzkHfYyNc" name="Noble Audio FoKus Artemis-7" alt="The Noble Audio FoKus Artemis in hand showing the controls" src="https://cdn.mos.cms.futurecdn.net/v2/t:103,l:0,cw:1660,ch:934,q:80/qkx54VX952ZtimzkHfYyNc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>For the Artemis, Noble did away with the Apollo’s detachable boom mic, which is a good thing. This is a set of ANC headphones, not a <a href="https://www.tomsguide.com/best-picks/best-gaming-headsets"><u>gaming headset</u></a>. The new internal mic does a fairly good job for calls. I called my wife, who reported my voice sounded clear and natural, if not a little bit thin. </p><p>She noted, however, that the Artemis mic picked up literally every single background noise, from rustling paper to unwrapping a parcel to merely pulling my keys from my pocket. Apparently this was very distracting, and with no environmental noise cancelation on the mic, there was nothing I could do about it.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-battery-life"><span>Noble Audio FoKus Artemis review: Battery life</span></h2><p>Noble promises 55 hours of battery life with ANC off and 35 with ANC on. This sits between the 38-hour and 30-hour ANC figures of the H95 and Px8 S2 respectively. It’s a lot better than the 20 hours you can expect from the AirPods Max 2.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1492px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="nTpVuqabnbxqeZNuxEyFQc" name="Noble Audio FoKus Artemis-13" alt="The Noble Audio FoKus Artemis with their earcups removed to show the drivers and removable battery" src="https://cdn.mos.cms.futurecdn.net/v2/t:284,l:271,cw:1492,ch:840,q:80/nTpVuqabnbxqeZNuxEyFQc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Real world results will of course vary based on your individual usage and whether you’re using USB-C for playback. In real terms, though, I used the Artemis extensively over a three-day press trip without charging them and was left with around 50% left, so there’s more than enough juice for a short trip away, and you’ll very likely be able to find a power socket in that time anyway.</p><p>The Artemis also have a neat trick up their sleeve: take off the left ear pad and you’ll see a battery held in place with a screw! That means you can replace the battery yourself, theoretically increasing the Artemis’ longevity. We’ve seen this before on the <a href="https://www.tomsguide.com/audio/headphones/sennheiser-momentum-5-review"><u>Sennheiser Momentum 5</u></a>, but it’s not commonplace. Of course, it relies on Noble still making these batteries whenever in the future you need to replace yours. Despite questionable upsides, though, there are certainly no downsides to this.</p><h2 class="article-body__section" id="section-noble-audio-fokus-artemis-review-verdict"><span>Noble Audio FoKus Artemis review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Vbxw55cC3N8m8tKJ8RQNCc" name="Noble Audio FoKus Artemis-5" alt="The Noble Audio FoKus Artemis on a stone surface, propped up on a plinth, against a blue background" src="https://cdn.mos.cms.futurecdn.net/Vbxw55cC3N8m8tKJ8RQNCc.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The FoKus Artemis aren’t perfect. When you’re being charged $899, you deserve a certain level of polish that, unfortunately, isn’t quite there in places. Admittedly, most of this is fairly minor, like the Artemis’ overly sensitive mic, or their lacking the sheer opulent finish and attention to detail of similarly priced rivals. The amateurism of the companion app is more worrying, though, given its importance to the headphones’ core operation. Thankfully that’s something which can be improved over time (and really needs to be, Noble, if you’re reading).</p><p>That said, I’m still head over heels in love with the FoKus Artemis, and there’s no question that they deserve my recommendation. Because <em>that</em> <em>sound</em> — mah gawd. It's what these cans are all about. Fire them up, sort out your personalized EQ, and you’ll quickly realize what you’ve paid for. The sound is simply exquisite, with detail, space, power in abundance, yet balance. The $900 finesse lacked elsewhere is made up for in triumphant fashion by just listening to the things. And that’s a compromise I can live with.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I just went hands-on with the world's smallest earbuds — here's what happened (and yes, they have ANC now) ]]></title>
                                                                                                <dc:content><![CDATA[ <p>When I tested the $39 <a href="https://www.tomsguide.com/reviews/Jlab-jbuds-mini">JLab JBuds Mini</a> last year, I thought they were nearly perfect. Definitely some of the <a href="http://tomsguide.com/us/best-wireless-earbuds,review-6160.html">best earbuds</a> you can buy. The one flaw? No ANC. </p><p>Thankfully, JLab has rectified that in 2026, and that's not the only thing we've been treated to. Not only do the new JLab Mini ANC have ANC (how could you tell?), they also come in a gorgeous splash of fun colors. No white here. Just earbuds that actually make me feel joyful. Oh, and here's a major plot twist — the earbuds have Apple-accredited Find My. As someone who loses her AirPods about six times a day, this is a lifesaver.</p><p>While they retail for $42.99, so around $3 more than the non-ANC version, these buds are still a steal. $43 for ANC, Find My, great sound quality, and a mini fit that doesn't hurt my ears? Sign. Me. Up. I tried them out at <a href="http://tomsguide.com/tag/ifa-2026">IFA 2026</a>, so here's what happened.</p><p>The JLab JBuds Mini ANC are 20.3 mm x 13.2 mm x 21.3 mm. I can't find any earbuds smaller than this, but there's always a chance they aren't the definite "smallest" earbuds.</p><h2 id="what-39-s-the-anc-actually-like">What's the ANC actually 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:2560px;"><p class="vanilla-image-block" style="padding-top:56.21%;"><img id="fd7xp4m5QTv8YJFmi3ip3h" name="jlab mini 3" alt="the jlab jbuds mini anc photographed on a white table" src="https://cdn.mos.cms.futurecdn.net/fd7xp4m5QTv8YJFmi3ip3h.jpg" mos="" align="middle" fullscreen="" width="2560" height="1439" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Alright, I'm not going to pretend these $43 miniature earbuds rival the <a href="https://www.tomsguide.com/audio/earbuds/bose-quietcomfort-ultra-earbuds-2-review">Bose QuietComfort Ultra Earbuds Gen 2</a> or anything, because that would be untrue. The ANC is good. I tested the earbuds in a busy conference hall, with people walking past me, people talking, and just general life noise. I could hear a <em>slight</em> hum of chatter beneath my music, but the buds blocked out 95% of all conversations and 100% of high-frequency sounds like my typing. </p><p>I haven't yet tested them on public transport, but if the performance in a loud convention center is anything to go by, I'll be in my own silent bubble on my journey home.</p><h2 id="and-sound-quality">And sound quality?</h2><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="7QWni42k2AFH6n5AM6dovg" name="jlab mini 1" alt="the jlab jbuds mini anc photographed on a white table" src="https://cdn.mos.cms.futurecdn.net/7QWni42k2AFH6n5AM6dovg.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Like the original JLab JBuds Mini, the sound quality is great for the price. Again, you're not getting AirPods Pro 3 quality here, but that's not the point. If you expect $250 sound quality on $43 earbuds, that's kind of on you. </p><p>In "Mandalay" by Amyl and the Sniffers, the grungey female vocal was textured and evocative, and the bass and percussion were pleasantly balanced. The instrument separation was okay, but the grubby guitars did tend to blend into one another. Even so, for $43, I was really impressed.</p><div><blockquote><p>For $43, I was really impressed</p></blockquote></div><p>The warm vocals in "Leave Me Alone" by Deki Alem flowed across the soundscape like a river of velvet, rich and comforting. There was a touch of dampening in the percussion, but nothing that would disappoint me for $43. </p><p>Finally, the frenetic, anxious guitars in "Coming on Strong" by Bloc Party raced around the soundstage, layered deftly under the hi-hats and vocals. Sure, the soundscape could have been wider, but again, these are $43 earbuds. I really don't think you can ask for much more — and I almost wouldn't want to. I'm so impressed with the JLab JBuds Mini ANC.</p><h2 id="final-thoughts">Final thoughts</h2><p>All I can think about is how excited I am to write my full, comprehensive review of the JLab JBuds Mini ANC. I was blown away by the performance — and the $43 pricetag — and I can't wait to get stuck into reviewing them.</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/sennheiser-momentum-5-true-wireless-review">I tested the Sennheiser Momentum 5 True Wireless — sorry AirPods, these are the earbuds to beat in 2026</a></li><li><a href="https://www.tomsguide.com/audio/headphones/i-tried-sonos-ace-ultra-and-it-does-something-your-bluetooth-headphones-cant">I tried Sonos Ace Ultra, and it does something your Bluetooth headphones can’t</a></li><li><a href="https://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></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/i-just-went-hands-on-with-the-worlds-smallest-earbuds-heres-what-happened-and-yes-they-have-anc-now</link>
                                                                            <description>
                            <![CDATA[ I went hands-on with the new JLab JBuds Mini ANC, and they're some of the most exciting earbuds of 2026. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">MtPcADzwzWx5kBZfMVL2iA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/PhkgzZfKcVeSn7NqwernJY-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 04 Sep 2026 11:08:40 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Earbuds]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/PhkgzZfKcVeSn7NqwernJY-1280-80.jpg">
                                                            <media:credit><![CDATA[Erin Bashford]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the jlab jbuds mini anc in champagne photographed with five pairs in the background]]></media:description>                                                            <media:text><![CDATA[the jlab jbuds mini anc in champagne photographed with five pairs in the background]]></media:text>
                                <media:title type="plain"><![CDATA[the jlab jbuds mini anc in champagne photographed with five pairs in the background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/PhkgzZfKcVeSn7NqwernJY-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>When I tested the $39 <a href="https://www.tomsguide.com/reviews/Jlab-jbuds-mini">JLab JBuds Mini</a> last year, I thought they were nearly perfect. Definitely some of the <a href="http://tomsguide.com/us/best-wireless-earbuds,review-6160.html">best earbuds</a> you can buy. The one flaw? No ANC. </p><p>Thankfully, JLab has rectified that in 2026, and that's not the only thing we've been treated to. Not only do the new JLab Mini ANC have ANC (how could you tell?), they also come in a gorgeous splash of fun colors. No white here. Just earbuds that actually make me feel joyful. Oh, and here's a major plot twist — the earbuds have Apple-accredited Find My. As someone who loses her AirPods about six times a day, this is a lifesaver.</p><p>While they retail for $42.99, so around $3 more than the non-ANC version, these buds are still a steal. $43 for ANC, Find My, great sound quality, and a mini fit that doesn't hurt my ears? Sign. Me. Up. I tried them out at <a href="http://tomsguide.com/tag/ifa-2026">IFA 2026</a>, so here's what happened.</p><p>The JLab JBuds Mini ANC are 20.3 mm x 13.2 mm x 21.3 mm. I can't find any earbuds smaller than this, but there's always a chance they aren't the definite "smallest" earbuds.</p><h2 id="what-39-s-the-anc-actually-like">What's the ANC actually 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:2560px;"><p class="vanilla-image-block" style="padding-top:56.21%;"><img id="fd7xp4m5QTv8YJFmi3ip3h" name="jlab mini 3" alt="the jlab jbuds mini anc photographed on a white table" src="https://cdn.mos.cms.futurecdn.net/fd7xp4m5QTv8YJFmi3ip3h.jpg" mos="" align="middle" fullscreen="" width="2560" height="1439" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Alright, I'm not going to pretend these $43 miniature earbuds rival the <a href="https://www.tomsguide.com/audio/earbuds/bose-quietcomfort-ultra-earbuds-2-review">Bose QuietComfort Ultra Earbuds Gen 2</a> or anything, because that would be untrue. The ANC is good. I tested the earbuds in a busy conference hall, with people walking past me, people talking, and just general life noise. I could hear a <em>slight</em> hum of chatter beneath my music, but the buds blocked out 95% of all conversations and 100% of high-frequency sounds like my typing. </p><p>I haven't yet tested them on public transport, but if the performance in a loud convention center is anything to go by, I'll be in my own silent bubble on my journey home.</p><h2 id="and-sound-quality">And sound quality?</h2><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="7QWni42k2AFH6n5AM6dovg" name="jlab mini 1" alt="the jlab jbuds mini anc photographed on a white table" src="https://cdn.mos.cms.futurecdn.net/7QWni42k2AFH6n5AM6dovg.jpg" mos="" align="middle" fullscreen="" width="2560" height="1440" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>Like the original JLab JBuds Mini, the sound quality is great for the price. Again, you're not getting AirPods Pro 3 quality here, but that's not the point. If you expect $250 sound quality on $43 earbuds, that's kind of on you. </p><p>In "Mandalay" by Amyl and the Sniffers, the grungey female vocal was textured and evocative, and the bass and percussion were pleasantly balanced. The instrument separation was okay, but the grubby guitars did tend to blend into one another. Even so, for $43, I was really impressed.</p><div><blockquote><p>For $43, I was really impressed</p></blockquote></div><p>The warm vocals in "Leave Me Alone" by Deki Alem flowed across the soundscape like a river of velvet, rich and comforting. There was a touch of dampening in the percussion, but nothing that would disappoint me for $43. </p><p>Finally, the frenetic, anxious guitars in "Coming on Strong" by Bloc Party raced around the soundstage, layered deftly under the hi-hats and vocals. Sure, the soundscape could have been wider, but again, these are $43 earbuds. I really don't think you can ask for much more — and I almost wouldn't want to. I'm so impressed with the JLab JBuds Mini ANC.</p><h2 id="final-thoughts">Final thoughts</h2><p>All I can think about is how excited I am to write my full, comprehensive review of the JLab JBuds Mini ANC. I was blown away by the performance — and the $43 pricetag — and I can't wait to get stuck into reviewing them.</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/sennheiser-momentum-5-true-wireless-review">I tested the Sennheiser Momentum 5 True Wireless — sorry AirPods, these are the earbuds to beat in 2026</a></li><li><a href="https://www.tomsguide.com/audio/headphones/i-tried-sonos-ace-ultra-and-it-does-something-your-bluetooth-headphones-cant">I tried Sonos Ace Ultra, and it does something your Bluetooth headphones can’t</a></li><li><a href="https://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></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested the Sennheiser Momentum 5 True Wireless — sorry AirPods, these are the earbuds to beat in 2026 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Sennhesier Momentum 5 True Wireless are the audio brand stalwart’s latest flagship Bluetooth earbuds — but I know the question on your lips is, “Well, are they <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best earbuds</a> of 2026?” Yes, the Momentum 5 TW are definitely in the running. </p><p>Thanks to the rich, complex, well-balanced, and vivacious sound quality, the Momentum 5 True Wireless are, without a doubt, one of the best-sounding sets I’ve had the pleasure of putting in my ears. Ever. Warmer than AirPods Pro 3 and XM6 but more nuanced than Bose QuietComfort Ultra Gen 2, the Momentum 5 TW are seriously ones to beat. </p><p>Not only do they sound fantastic over run of the mill codecs, they also come packed with aptX Lossless and Dolby Atmos head tracking for summer immersive movies. Might these be the perfect earbuds for you? To find out, keep reading this Sennheiser Momentum 5 True Wireless review. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-specs"><span>Sennheiser Momentum 5 True Wireless 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/Sennheiser-Momentum-Wireless-Bluetooth-Graphite/dp/B0HBGKNJ75" target="_blank" rel="nofollow">$299</a> / <a href="https://www.amazon.co.uk/Sennheiser-MOMENTUM-Wireless-Bluetooth-Earbuds-Denim/dp/B0HC72BMGT" target="_blank" rel="nofollow">£249</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>7mm proprietary</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz - 20,000Hz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP54</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 6.0</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>Yes </p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>12 hours (buds) / 40 hours (case) ANC off</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.2 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Gray, cream, copper, dark blue, and purple</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-price-availability"><span>Sennheiser Momentum 5 True Wireless 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="cPLMcwgAsA3tjw9DXN3oRA" name="Sennheiser_momentum01.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/cPLMcwgAsA3tjw9DXN3oRA.jpg" 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 Sennheiser Momentum 5 True Wireless earbuds are <a href="https://www.amazon.com/Sennheiser-Momentum-Wireless-Bluetooth-Graphite/dp/B0HBGKNJ75" target="_blank" rel="nofollow">$299 from Amazon U.S.</a> and <a href="https://www.amazon.co.uk/Sennheiser-MOMENTUM-Wireless-Bluetooth-Earbuds-Denim/dp/B0HC72BMGT" target="_blank" rel="nofollow">£249 from Amazon U.K.</a></p><p>This places the earbuds within the premium lifestyle bracket. Think your <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review">Apple AirPods Pro 3</a>, your <a href="https://www.tomsguide.com/audio/earbuds/samsung-galaxy-buds-4-pro-review">Samsung Galaxy Buds 4 Pro</a>, your <a href="https://www.tomsguide.com/audio/earbuds/bose-quietcomfort-ultra-earbuds-2-review">Bose QuietComfort Ultra Earbuds Gen 2</a>, your <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-1000xm6-review">Sony WF-1000XM6</a>. I’ve also recently tested the <a href="https://www.tomsguide.com/audio/earbuds/status-pro-x-review">Status Audio Pro X</a>, which sound utterly divine, and the <a href="https://www.tomsguide.com/audio/earbuds/technics-eah-az100-wireless-earbuds-review">Technics AZ100</a>, another non-big-name high performer. </p><p>For your $299, you get excellent sound, including aptX Lossless support, Snapdragon Sound, and Dolby Atmos-optimized head tracking (great for movies), and lovely ANC. While $300 is, objectively, a large amount of money, this price is expected for flagship Sennheiser wireless earbuds. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-design"><span>Sennheiser Momentum 5 True Wireless 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="Rf4ARBGuZKoF866CFT3nh9" name="Sennheiser Momentum 5 True Wireless 5_ 2.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Rf4ARBGuZKoF866CFT3nh9.jpg" 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 Sennheiser Momentum 5 True Wireless are your typical bean-shaped earbuds, with a large nozzle that sits in your concha. This is great for those who don’t get on with stem designs, like AirPods Pro 3. If you’re more of the Bose QC Ultra Gen 2 or Sony WF-1000XM6 persuasion, you’ll like the Sennheiser Momentum 5 TW. </p><p>I tested the lovely dark blue (“Denim”) colorway, as you can see from the images, which is sleek and premium. I have nothing bad to say about the aesthetics of these earbuds. </p><p>You also get five tip sizes — they’re all silicon with no foam buds, so sorry to any silicon haters in the house. I used the smallest size because I have weirdly small ears… subtle foreshadowing. </p><p>The buds themselves are IP54-rated, which isn’t as waterproof as the AirPods Pro 3’s IP55, but better than Bose QC Ultra Earbuds Gen 2’s IPX4 rating. In real terms, this means you’d be able to wear the Sennheisers on a run or during a light rain shower without breaking them. </p><h2 id="comfort-3">Comfort</h2><p>If you’ve got small ears, the Momentum 5 TW won’t be for you. The bean-shaped nozzle is so large that it made my ears ache after around five minutes. However, my normal-ear-sized colleagues reported that they were comfortable, so this is solely a small-ear issue. </p><h2 id="controls-6">Controls </h2><p>Controls are easy and customizable. A single tap pauses the music on the right ear and adjusts ANC on the left ear. A double tap skips ahead, and a long press changes volume. Of course, if you don’t like these default commands, you can customize them in the Sennheiser Smart Control Plus app. </p><h2 id="connectivity-6">Connectivity</h2><p>I was incredibly pleased to see Bluetooth 6.0 on the Sennheiser Momentum 5 True Wireless. You’d think in 2026 all new earbuds would release with the latest Bluetooth version, but this is not the case. Apple launched the long-awaited <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">AirPods Max 2</a> with 2021’s Bluetooth 5.3. Eesh. </p><p>Thankfully, Sennheiser has bucked this trend and “treated” us to Bluetooth 6.0. In real terms, this means it’s more reliable, has a longer range, and can transmit higher-res files faster. I was able to get out of my apartment and up the stairs before the earbuds disconnected from my <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-16-pro-review">iPhone 16 Pro</a>. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-sound-quality"><span>Sennheiser Momentum 5 True Wireless review: Sound quality</span></h2><p>To test sound quality, I listened to a range of genres on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, our <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles. </p><p>Spoiler alert: the Sennheiser Momentum 5 True Wireless sound awesome. </p><p>First, I played 'Roslyn' by Bon Iver and St. Vincent. I could feel every vibration of the acoustic guitar, each chord change reverberating in my brain. The layered vocals were dreamy, atmospheric, and evocative, and the bass notes flowed along the side of the soundscape beautifully. </p><p>Next, I switched up genre to 'iPod Touch’ by Ninajirachi. The deep synth bassline thumped through the earbuds without overpowering the higher-pitched female vocals or the snapping hi-hats. Each housey piano chord was layered gracefully underneath the vocals and melted into the mid-track EDM breakdown. In this section, the glitchy, over-processed bass felt alive, and every squeaky sound effect was present over top of the soundscape. This song sounded awesome. Deep bass was also powerful yet balanced in ‘Wifey Riddim (George Daniel remix)” by Shygirl and 'space cadet' by sim0ne. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NXJfjP8NSrfDwkxaTeoAFA" name="Sennheiser Momentum 5 True Wireless 5_ 5.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/NXJfjP8NSrfDwkxaTeoAFA.jpg" 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>Finally, I went for something heavier. 'Punishers' by Die Spitz is an atmospheric guitar-led track with immersive growly guitars and crunchy bass. The sub-bass was clear and controlled with zero grinding or cutting out. The female vocals were powerful and layered perfectly at the top of the track, with zero tinniness from the snare drums or cymbals. On top of this, the soundscape was expansive and wide, and truly immersed me in the music. </p><p>To test the Dolby Atmos head tracking, I watched 'Twilight' on Apple TV. The Dolby Atmos performance was intriguing, but it didn’t impress me enough to claim I can’t live without it. I could hear dialogue from the far edges of the soundstage, and footsteps sounded like they were coming from below me. Of course, this is a nice feature to have, but I’m not sure I would get much use out of it. If you’re an avid laptop/phone movie watcher, this is a fantastic feature, but it’s not really for me.</p><p>Overall, the Momentum 5 TW sound excellent. Given Sennheiser’s reputation, you’d expect its earbuds to sound good, but I can attest for it right now: the Momentum 5 TW are richer than AirPods Pro 3, and warmer than Sony WF-1000XM6. They’re more detailed than Bose QuietComfort Ultra Earbuds Gen 2, and just as fun and vivacious as the Status Audio Pro X. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-anc"><span>Sennheiser Momentum 5 True Wireless review: 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YWhPARhq4bHAXXqrXJzbUA" name="Sennheiser_momentum03.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/YWhPARhq4bHAXXqrXJzbUA.jpg" 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 ANC on the Momentum 5 True Wireless is good, but not jaw-droppingly good. If you’re after “ANC so good you feel lonely” (thanks to my colleague Nikita for the everlastingly good quote), you’d be better off with the Bose QC Ultra Gen 2. </p><p>All that being said, the Momentum 5 True Wireless still block out the majority of environmental noise. When I was walking around the city, I could hear low frequency rumbling engines and some high-frequency sounds like car horns, but most chatter was blocked out. When I tested with a city noise track on YouTube played at 67dB, I couldn’t hear the track at all. </p><p>This is more than enough for everyday usage; if you’re needing earbuds for commuting or office work, you’ll be fine. If you live next door to a construction site, though, I might recommend the Bose instead. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-call-quality"><span>Sennheiser Momentum 5 True Wireless review: Call quality</span></h2><p>To test call quality, I called my boyfriend. As mentioned earlier, I love how the earbuds switch to transparency during phone calls; this means I can actually hear the volume of my voice. The earbuds also announce when the call is ended, which is a nice touch. </p><p>He reported my voice sounded perfectly clear, with zero metallicness or echoing. When I played a city white noise track at max volume (from my laptop), he couldn’t hear it at all. This is a fantastic performance; I’m really impressed.</p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-app"><span>Sennheiser Momentum 5 True Wireless review: App</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nwR7sYyEpD82dG3fgxvwr9" name="Sennheiser Momentum 5 True Wireless 5_ 6.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/nwR7sYyEpD82dG3fgxvwr9.jpg" 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 Sennheiser Momentum 5 TW use the Sennheiser SmartControlPlus app, which is fantastic. The layout is clean and easily navigable, and the app itself has a plethora of interesting, effective functions. You can test the ear tip fit, make a personalized EQ, activate head tracking, rename the earbuds, and control transparency levels. </p><p>As mentioned in “Call quality”, you can customize the earbuds to turn off ANC during phone calls, which is a useful feature for walking around the city. </p><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="U4KHnDh6MamwVqbbLwTSXk" name="sennheiser momentum 5 true wireless app screenshots" alt="three screenshots from the sennheiser smart control app showing functionality with the sennheiser momentum 5 true wireless earbuds" src="https://cdn.mos.cms.futurecdn.net/U4KHnDh6MamwVqbbLwTSXk.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 / Sennheiser)</span></figcaption></figure><p>A new feature is “Sound zones”, which is like Sony’s Adaptive Sound Control. In short, you can set the earbuds to turn ANC on or off depending on your location. In theory, you could turn off ANC as soon as you arrive home, or turn on ANC when you arrive at the bus stop. I don’t really lean towards these kinds of features — I’m more than happy to simply tap the touch control on the earbud — but it’s nice to have if you’re the sound-zone-inclined. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-battery"><span>Sennheiser Momentum 5 True Wireless review: 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NTvfjDRkAVe7fsaJBfNU2A" name="Sennheiser Momentum 5 True Wireless 5_ 3.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/NTvfjDRkAVe7fsaJBfNU2A.jpg" 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’s one for the eco-conscious amongst you: the Sennheiser Momentum 5 True Wireless have replaceable batteries. Yes, replaceable batteries! Just like the new Sennheiser Momentum 5 headphones, which I reviewed earlier this year, you can also replace the batteries on the TW earbuds. </p><p>Sennheiser rated the battery for up to 12 hours in the buds and then 40 hours overall in the charging case. I tested the Sennheiser Momentum 5 for a whole weekend and then three working days, and I had to recharge the case once. I did have all the bells and whistles turned on, though: Dolby Atmos head tracking, ANC on max, playing with all the features like custom EQ. I’m sure normal listening wouldn’t use this much battery. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-verdict"><span>Sennheiser Momentum 5 True Wireless 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="qNKvF6oKCPyYkbQ8DHdgX9" name="Sennheiser Momentum 5 True Wireless 5_ 4.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/qNKvF6oKCPyYkbQ8DHdgX9.jpg" 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’m really, really impressed with the Sennheiser Momentum 5 True Wireless earbuds. Not only do they sound divine, they also boast a multitude of fun, useful features like head tracking, integrated Dolby Atmos, personalized sound, and sound zones. For me, sound quality is King, and I’m pleased to report that if you’re a fellow sound snob, you’ll get on just fine with these buds. </p><p>While I wish the fit was more accommodating for fellow tiny-eared folk, “it’s not you, it’s me” is actually relevant here — normal-to-large-size ears will be fine with the snug bean-shaped fit. Unfortunately, I won’t be replacing my AirPods Pro 2 with the Momentum 5 TW due to the size issue, but it’s a me problem. </p><p>If you value premium sound quality, a sleek design, and a bunch of cool features, the Sennheiser Momentum 5 True Wireless will be the earbuds for you. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/sennheiser-momentum-5-true-wireless-review</link>
                                                                            <description>
                            <![CDATA[ The Sennheiser Momentum 5 True Wireless earbuds pack a major punch, with excellent sound quality, Dolby Atmos for movies, and a sleek design. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">XpfNvQM6wpraWd6uaszksY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/Dut2kQ6MCnYyYpcz2V9gF9-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 03 Sep 2026 10:05:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Earbuds]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ erin.bashford@futurenet.com (Erin Bashford) ]]></author>                    <dc:creator><![CDATA[ Erin Bashford ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/rLvJvJVZx43hEzSsJy3BpL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Erin Bashford is a senior reviews writer at Tom’s Guide. She has a Master’s in Broadcast and Digital Journalism from the University of East Anglia and 7 years of experience reviewing music and events for various publications. She has edited publications such as Outline Magazine’s Guide to Norwich, and she has written for a number of music magazines and websites such as Clash Magazine, Outline Magazine and Dork Magazine. She has a strong interest in audio gear and the music world. &lt;/p&gt;&lt;p&gt;As an ex-barista, Erin is passionate about coffee tech. She also loves finding the best cooking hacks and kitchen appliances, including her beloved Instant Pot. &lt;/p&gt;&lt;p&gt;In her spare time, you can find her reading, practising yoga, hiking, writing fantasy novels, or stressing over NYT Games.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/Dut2kQ6MCnYyYpcz2V9gF9-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom&#039;s guide background]]></media:description>                                                            <media:text><![CDATA[the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom&#039;s guide background]]></media:text>
                                <media:title type="plain"><![CDATA[the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom&#039;s guide background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/Dut2kQ6MCnYyYpcz2V9gF9-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Sennhesier Momentum 5 True Wireless are the audio brand stalwart’s latest flagship Bluetooth earbuds — but I know the question on your lips is, “Well, are they <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best earbuds</a> of 2026?” Yes, the Momentum 5 TW are definitely in the running. </p><p>Thanks to the rich, complex, well-balanced, and vivacious sound quality, the Momentum 5 True Wireless are, without a doubt, one of the best-sounding sets I’ve had the pleasure of putting in my ears. Ever. Warmer than AirPods Pro 3 and XM6 but more nuanced than Bose QuietComfort Ultra Gen 2, the Momentum 5 TW are seriously ones to beat. </p><p>Not only do they sound fantastic over run of the mill codecs, they also come packed with aptX Lossless and Dolby Atmos head tracking for summer immersive movies. Might these be the perfect earbuds for you? To find out, keep reading this Sennheiser Momentum 5 True Wireless review. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-specs"><span>Sennheiser Momentum 5 True Wireless 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/Sennheiser-Momentum-Wireless-Bluetooth-Graphite/dp/B0HBGKNJ75" target="_blank" rel="nofollow">$299</a> / <a href="https://www.amazon.co.uk/Sennheiser-MOMENTUM-Wireless-Bluetooth-Earbuds-Denim/dp/B0HC72BMGT" target="_blank" rel="nofollow">£249</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>7mm proprietary</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz - 20,000Hz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP54</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 6.0</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>Yes </p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>12 hours (buds) / 40 hours (case) ANC off</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.2 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Gray, cream, copper, dark blue, and purple</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-price-availability"><span>Sennheiser Momentum 5 True Wireless 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="cPLMcwgAsA3tjw9DXN3oRA" name="Sennheiser_momentum01.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/cPLMcwgAsA3tjw9DXN3oRA.jpg" 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 Sennheiser Momentum 5 True Wireless earbuds are <a href="https://www.amazon.com/Sennheiser-Momentum-Wireless-Bluetooth-Graphite/dp/B0HBGKNJ75" target="_blank" rel="nofollow">$299 from Amazon U.S.</a> and <a href="https://www.amazon.co.uk/Sennheiser-MOMENTUM-Wireless-Bluetooth-Earbuds-Denim/dp/B0HC72BMGT" target="_blank" rel="nofollow">£249 from Amazon U.K.</a></p><p>This places the earbuds within the premium lifestyle bracket. Think your <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review">Apple AirPods Pro 3</a>, your <a href="https://www.tomsguide.com/audio/earbuds/samsung-galaxy-buds-4-pro-review">Samsung Galaxy Buds 4 Pro</a>, your <a href="https://www.tomsguide.com/audio/earbuds/bose-quietcomfort-ultra-earbuds-2-review">Bose QuietComfort Ultra Earbuds Gen 2</a>, your <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-1000xm6-review">Sony WF-1000XM6</a>. I’ve also recently tested the <a href="https://www.tomsguide.com/audio/earbuds/status-pro-x-review">Status Audio Pro X</a>, which sound utterly divine, and the <a href="https://www.tomsguide.com/audio/earbuds/technics-eah-az100-wireless-earbuds-review">Technics AZ100</a>, another non-big-name high performer. </p><p>For your $299, you get excellent sound, including aptX Lossless support, Snapdragon Sound, and Dolby Atmos-optimized head tracking (great for movies), and lovely ANC. While $300 is, objectively, a large amount of money, this price is expected for flagship Sennheiser wireless earbuds. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-design"><span>Sennheiser Momentum 5 True Wireless 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="Rf4ARBGuZKoF866CFT3nh9" name="Sennheiser Momentum 5 True Wireless 5_ 2.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/Rf4ARBGuZKoF866CFT3nh9.jpg" 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 Sennheiser Momentum 5 True Wireless are your typical bean-shaped earbuds, with a large nozzle that sits in your concha. This is great for those who don’t get on with stem designs, like AirPods Pro 3. If you’re more of the Bose QC Ultra Gen 2 or Sony WF-1000XM6 persuasion, you’ll like the Sennheiser Momentum 5 TW. </p><p>I tested the lovely dark blue (“Denim”) colorway, as you can see from the images, which is sleek and premium. I have nothing bad to say about the aesthetics of these earbuds. </p><p>You also get five tip sizes — they’re all silicon with no foam buds, so sorry to any silicon haters in the house. I used the smallest size because I have weirdly small ears… subtle foreshadowing. </p><p>The buds themselves are IP54-rated, which isn’t as waterproof as the AirPods Pro 3’s IP55, but better than Bose QC Ultra Earbuds Gen 2’s IPX4 rating. In real terms, this means you’d be able to wear the Sennheisers on a run or during a light rain shower without breaking them. </p><h2 id="comfort-3">Comfort</h2><p>If you’ve got small ears, the Momentum 5 TW won’t be for you. The bean-shaped nozzle is so large that it made my ears ache after around five minutes. However, my normal-ear-sized colleagues reported that they were comfortable, so this is solely a small-ear issue. </p><h2 id="controls-6">Controls </h2><p>Controls are easy and customizable. A single tap pauses the music on the right ear and adjusts ANC on the left ear. A double tap skips ahead, and a long press changes volume. Of course, if you don’t like these default commands, you can customize them in the Sennheiser Smart Control Plus app. </p><h2 id="connectivity-6">Connectivity</h2><p>I was incredibly pleased to see Bluetooth 6.0 on the Sennheiser Momentum 5 True Wireless. You’d think in 2026 all new earbuds would release with the latest Bluetooth version, but this is not the case. Apple launched the long-awaited <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">AirPods Max 2</a> with 2021’s Bluetooth 5.3. Eesh. </p><p>Thankfully, Sennheiser has bucked this trend and “treated” us to Bluetooth 6.0. In real terms, this means it’s more reliable, has a longer range, and can transmit higher-res files faster. I was able to get out of my apartment and up the stairs before the earbuds disconnected from my <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-16-pro-review">iPhone 16 Pro</a>. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-sound-quality"><span>Sennheiser Momentum 5 True Wireless review: Sound quality</span></h2><p>To test sound quality, I listened to a range of genres on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, our <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles. </p><p>Spoiler alert: the Sennheiser Momentum 5 True Wireless sound awesome. </p><p>First, I played 'Roslyn' by Bon Iver and St. Vincent. I could feel every vibration of the acoustic guitar, each chord change reverberating in my brain. The layered vocals were dreamy, atmospheric, and evocative, and the bass notes flowed along the side of the soundscape beautifully. </p><p>Next, I switched up genre to 'iPod Touch’ by Ninajirachi. The deep synth bassline thumped through the earbuds without overpowering the higher-pitched female vocals or the snapping hi-hats. Each housey piano chord was layered gracefully underneath the vocals and melted into the mid-track EDM breakdown. In this section, the glitchy, over-processed bass felt alive, and every squeaky sound effect was present over top of the soundscape. This song sounded awesome. Deep bass was also powerful yet balanced in ‘Wifey Riddim (George Daniel remix)” by Shygirl and 'space cadet' by sim0ne. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NXJfjP8NSrfDwkxaTeoAFA" name="Sennheiser Momentum 5 True Wireless 5_ 5.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/NXJfjP8NSrfDwkxaTeoAFA.jpg" 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>Finally, I went for something heavier. 'Punishers' by Die Spitz is an atmospheric guitar-led track with immersive growly guitars and crunchy bass. The sub-bass was clear and controlled with zero grinding or cutting out. The female vocals were powerful and layered perfectly at the top of the track, with zero tinniness from the snare drums or cymbals. On top of this, the soundscape was expansive and wide, and truly immersed me in the music. </p><p>To test the Dolby Atmos head tracking, I watched 'Twilight' on Apple TV. The Dolby Atmos performance was intriguing, but it didn’t impress me enough to claim I can’t live without it. I could hear dialogue from the far edges of the soundstage, and footsteps sounded like they were coming from below me. Of course, this is a nice feature to have, but I’m not sure I would get much use out of it. If you’re an avid laptop/phone movie watcher, this is a fantastic feature, but it’s not really for me.</p><p>Overall, the Momentum 5 TW sound excellent. Given Sennheiser’s reputation, you’d expect its earbuds to sound good, but I can attest for it right now: the Momentum 5 TW are richer than AirPods Pro 3, and warmer than Sony WF-1000XM6. They’re more detailed than Bose QuietComfort Ultra Earbuds Gen 2, and just as fun and vivacious as the Status Audio Pro X. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-anc"><span>Sennheiser Momentum 5 True Wireless review: 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YWhPARhq4bHAXXqrXJzbUA" name="Sennheiser_momentum03.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/YWhPARhq4bHAXXqrXJzbUA.jpg" 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 ANC on the Momentum 5 True Wireless is good, but not jaw-droppingly good. If you’re after “ANC so good you feel lonely” (thanks to my colleague Nikita for the everlastingly good quote), you’d be better off with the Bose QC Ultra Gen 2. </p><p>All that being said, the Momentum 5 True Wireless still block out the majority of environmental noise. When I was walking around the city, I could hear low frequency rumbling engines and some high-frequency sounds like car horns, but most chatter was blocked out. When I tested with a city noise track on YouTube played at 67dB, I couldn’t hear the track at all. </p><p>This is more than enough for everyday usage; if you’re needing earbuds for commuting or office work, you’ll be fine. If you live next door to a construction site, though, I might recommend the Bose instead. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-call-quality"><span>Sennheiser Momentum 5 True Wireless review: Call quality</span></h2><p>To test call quality, I called my boyfriend. As mentioned earlier, I love how the earbuds switch to transparency during phone calls; this means I can actually hear the volume of my voice. The earbuds also announce when the call is ended, which is a nice touch. </p><p>He reported my voice sounded perfectly clear, with zero metallicness or echoing. When I played a city white noise track at max volume (from my laptop), he couldn’t hear it at all. This is a fantastic performance; I’m really impressed.</p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-app"><span>Sennheiser Momentum 5 True Wireless review: App</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nwR7sYyEpD82dG3fgxvwr9" name="Sennheiser Momentum 5 True Wireless 5_ 6.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/nwR7sYyEpD82dG3fgxvwr9.jpg" 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 Sennheiser Momentum 5 TW use the Sennheiser SmartControlPlus app, which is fantastic. The layout is clean and easily navigable, and the app itself has a plethora of interesting, effective functions. You can test the ear tip fit, make a personalized EQ, activate head tracking, rename the earbuds, and control transparency levels. </p><p>As mentioned in “Call quality”, you can customize the earbuds to turn off ANC during phone calls, which is a useful feature for walking around the city. </p><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="U4KHnDh6MamwVqbbLwTSXk" name="sennheiser momentum 5 true wireless app screenshots" alt="three screenshots from the sennheiser smart control app showing functionality with the sennheiser momentum 5 true wireless earbuds" src="https://cdn.mos.cms.futurecdn.net/U4KHnDh6MamwVqbbLwTSXk.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 / Sennheiser)</span></figcaption></figure><p>A new feature is “Sound zones”, which is like Sony’s Adaptive Sound Control. In short, you can set the earbuds to turn ANC on or off depending on your location. In theory, you could turn off ANC as soon as you arrive home, or turn on ANC when you arrive at the bus stop. I don’t really lean towards these kinds of features — I’m more than happy to simply tap the touch control on the earbud — but it’s nice to have if you’re the sound-zone-inclined. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-battery"><span>Sennheiser Momentum 5 True Wireless review: 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NTvfjDRkAVe7fsaJBfNU2A" name="Sennheiser Momentum 5 True Wireless 5_ 3.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/NTvfjDRkAVe7fsaJBfNU2A.jpg" 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’s one for the eco-conscious amongst you: the Sennheiser Momentum 5 True Wireless have replaceable batteries. Yes, replaceable batteries! Just like the new Sennheiser Momentum 5 headphones, which I reviewed earlier this year, you can also replace the batteries on the TW earbuds. </p><p>Sennheiser rated the battery for up to 12 hours in the buds and then 40 hours overall in the charging case. I tested the Sennheiser Momentum 5 for a whole weekend and then three working days, and I had to recharge the case once. I did have all the bells and whistles turned on, though: Dolby Atmos head tracking, ANC on max, playing with all the features like custom EQ. I’m sure normal listening wouldn’t use this much battery. </p><h2 class="article-body__section" id="section-sennheiser-momentum-5-true-wireless-review-verdict"><span>Sennheiser Momentum 5 True Wireless 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="qNKvF6oKCPyYkbQ8DHdgX9" name="Sennheiser Momentum 5 True Wireless 5_ 4.JPG" alt="the sennheiser momentum 5 true wireless earbuds in navy photographed with the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/qNKvF6oKCPyYkbQ8DHdgX9.jpg" 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’m really, really impressed with the Sennheiser Momentum 5 True Wireless earbuds. Not only do they sound divine, they also boast a multitude of fun, useful features like head tracking, integrated Dolby Atmos, personalized sound, and sound zones. For me, sound quality is King, and I’m pleased to report that if you’re a fellow sound snob, you’ll get on just fine with these buds. </p><p>While I wish the fit was more accommodating for fellow tiny-eared folk, “it’s not you, it’s me” is actually relevant here — normal-to-large-size ears will be fine with the snug bean-shaped fit. Unfortunately, I won’t be replacing my AirPods Pro 2 with the Momentum 5 TW due to the size issue, but it’s a me problem. </p><p>If you value premium sound quality, a sleek design, and a bunch of cool features, the Sennheiser Momentum 5 True Wireless will be the earbuds for you. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ 15 iconic electronic music albums that belong on your turntable ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Electronic music remains one of the most diverse and forward-thinking genres around. That loose term covers everything from party-starting dance music to vibey ambience that's perfect for kicking back and relaxing. Then there are some albums that remain uncategorisably strange and elusive.</p><p>For this list I've picked out 15 classic albums that I love, all of which are available right now on vinyl and listed in order of price. There are canonical classics here, like Kraftwerk's The Man Machine and Aphex Twin's first Selected Ambient Works LP; floor fillers like Daft Punk's Discovery; modern pop classics like Charli's Brat, and much more.</p><p>Really, though, this list could have been five times the length and still not featured every great electronic album. Check out the list below and see if any of your favorites are there.</p>        <div class="featured_product_block featured_block_hero" data-id="1b120f64-a6e3-11f1-be8d-c3b8a366d8d6">            <a href="https://www.amazon.com/Man-Machine-Kraftwerk/dp/B00EZ3CN6C/" data-model-name="Kraftwerk – The Man-Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:99.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Xc63C9dBoc5pCXekPMaVDo.jpg" alt="The Man-Machine"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Kraftwerk – The Man-Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121496-a6e3-11f1-93b4-af1280aa57e0">            <a href="https://www.amazon.com/Arca/dp/B06WVPSP92/" data-model-name="Arca – Arca" 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/9YoqEjBbsGuxyprmdXRWd4.jpg" alt="Arca"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Arca – Arca</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121644-a6e3-11f1-9709-61c2f4fb993e">            <a href="https://www.amazon.com/BRAT-Charli-XCX/dp/B0CZ9TWB5K/" data-model-name="Charli xcx – Brat" 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/rHYZn6YmFUNDRBcM9u8NzV.jpg" alt="ATLANTIC RECORDS, Charli xcx – Brat"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Charli xcx – Brat</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b120eba-a6e3-11f1-98bc-a92dec7367c6">            <a href="https://www.amazon.com/Selected-Ambient-Works-85-92/dp/B00E8K2I9Y/" data-model-name="Aphex Twin – Selected Ambient Works 85-92" 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/fogjFPBMx9uVGxJAU87Xkn.jpg" alt="Selected Ambient Works 85-92"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Aphex Twin – Selected Ambient Works 85-92</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121374-a6e3-11f1-a8b6-9fff81cd476c">            <a href="https://www.amazon.com/Dig-Your-Own-Hole-Reissue/dp/B00004WPG5/" data-model-name="Chemical Brothers – Dig Your Own Hole" 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/PaaJLdU4EN86UJQxN3SN74.jpg" alt="Dig Your Own Hole [2 Lp] [reissue]"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Chemical Brothers – Dig Your Own Hole</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b1216d0-a6e3-11f1-8104-b13fac8ebd0b">            <a href="https://www.amazon.com/Homogenic-Vinyl-Bj%C3%B6rk/dp/B00004WSZ2/" data-model-name="Bjork – Homogenic" 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/jcEttQjtykzm6tdCZji8t5.jpg" alt="Homogenic [vinyl]"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Bjork – Homogenic</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b1215b8-a6e3-11f1-973b-330ac1f493d3">            <a href="https://www.amazon.com/MOTHER-EARTHS-PLANTASIA-Mort-Garson/dp/B07QSH9FY8/" data-model-name="Mother Earth's Plantasia" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:97.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rBBTtzeHP3nZFSbwiZUAK5.jpg" alt="Mother Earth's Plantasia"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Mother Earth's Plantasia</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121252-a6e3-11f1-8084-1d24bdfa65e2">            <a href="https://www.amazon.com/Amber-AUTECHRE/dp/B01M08H4WR/" data-model-name="Autechre – Amber" 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/GzSik65pjWbhurMhWaKnp.jpg" alt="Amber"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Autechre – Amber</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b1212e8-a6e3-11f1-8ec3-f169297009fb">            <a href="https://www.amazon.com/Rounds-Reissue-Four-Tet/dp/B00BSWMJ8K/" data-model-name="Four Tet – Rounds" 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/xM3CzVtF5HYkhuLdpHScs3.jpg" alt="Rounds"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Four Tet – Rounds</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b12140a-a6e3-11f1-9c3a-e75bce8a906c">            <a href="https://www.amazon.com/Sound-Silver-Vinyl-LCD-SOUNDSYSTEM/dp/B000M34538/" data-model-name="LCD Soundsystem – Sound of Silver" 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/w5sRKffmHb3yVHXYdk6aP4.jpg" alt="Sound of Silver [vinyl]"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">LCD Soundsystem – Sound of Silver</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b1211c6-a6e3-11f1-8afb-a33caa0f378b">            <a href="https://www.amazon.com/Discovery-Daft-Punk/dp/B099ZRXZ6Q/" data-model-name="Daft Punk. – Discovery" 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/nDoi3zmzBBBRF3Y3CYr2xP.jpg" alt="ADA, Daft Punk. – Discovery"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Daft Punk. – Discovery</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b12152c-a6e3-11f1-819c-0fdd7aaa1a68">            <a href="https://www.amazon.com/Jazz-Frunk-Greats-THROBBING-GRISTLE/dp/B07H62ZCKM/" data-model-name="Throbbing Gristle – 20 Jazz Frunk Greats" 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/wbFsVDWarRQKb7FU6HEw75.jpg" alt="20 Jazz Frunk Greats"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Throbbing Gristle – 20 Jazz Frunk Greats</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b12109a-a6e3-11f1-8a1e-b332a8df3621">            <a href="https://www.amazon.com/Prodigy-Music-Jilted-Generation/dp/B0GKWWDPVR/" data-model-name="The Prodigy – Music for the Jilted Generation" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:103.73%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kEFNB5dSmxUrnmof2A9yUo.jpg" alt="Prodigy - Music for the Jilted Generation"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">The Prodigy – Music for the Jilted Generation</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121004-a6e3-11f1-81b8-df5b45f2a641">            <a href="https://www.amazon.com/Deep-Space-Model-500/dp/B0DL3NPBNS/" data-model-name="Model 500 – Deep Space" 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/jfhf9V7anr8vksVBAAhCCo.jpg" alt="Deep Space"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Model 500 – Deep Space</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121130-a6e3-11f1-b318-c9038bcb4f2c">            <a href="https://www.amazon.com/Oil-Every-Pearls-Insides-VINYL/dp/B07HBR6LVS/" data-model-name="Oil of Every Pearl's Un-Insides" 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/NTfRnQFmncNySv5J6GaWD.jpg" alt="Oil of Every Pearl's Un-Insides"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Oil of Every Pearl's Un-Insides</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/15-iconic-electronic-music-albums-that-belong-on-your-turntable</link>
                                                                            <description>
                            <![CDATA[ 15 of the best electronic music albums from a range of sub-genres, all of which are available to buy now on vinyl, from futuristic pop to dancefloor classics. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">PEbTUApzPHyx72X9heMop6</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/AE75Ae8UG2hJWqMnQAKUsA-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 02 Sep 2026 18:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Will Salmon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/HpRWP7Mh9ErgRGoTvZBbBJ.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Will Salmon is Tom&#039;s Guide&#039;s Collections Editor, curating advice on the best products to spend your money on. Will has been writing about tech and entertainment for almost 20 years, including stints as GamesRadar+&#039;s Streaming Editor, editing comics website Newsarama, and writing for lots and lots of magazines.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/AE75Ae8UG2hJWqMnQAKUsA-1280-80.jpg">
                                                            <media:credit><![CDATA[Redeye Worldwide, ADA, Ruthless Records / Warp, Warner Music International, Transgressive / Edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[xxxxx]]></media:description>                                                            <media:text><![CDATA[xxxxx]]></media:text>
                                <media:title type="plain"><![CDATA[xxxxx]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/AE75Ae8UG2hJWqMnQAKUsA-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Electronic music remains one of the most diverse and forward-thinking genres around. That loose term covers everything from party-starting dance music to vibey ambience that's perfect for kicking back and relaxing. Then there are some albums that remain uncategorisably strange and elusive.</p><p>For this list I've picked out 15 classic albums that I love, all of which are available right now on vinyl and listed in order of price. There are canonical classics here, like Kraftwerk's The Man Machine and Aphex Twin's first Selected Ambient Works LP; floor fillers like Daft Punk's Discovery; modern pop classics like Charli's Brat, and much more.</p><p>Really, though, this list could have been five times the length and still not featured every great electronic album. Check out the list below and see if any of your favorites are there.</p>        <div class="featured_product_block featured_block_hero" data-id="1b120f64-a6e3-11f1-be8d-c3b8a366d8d6">            <a href="https://www.amazon.com/Man-Machine-Kraftwerk/dp/B00EZ3CN6C/" data-model-name="Kraftwerk – The Man-Machine" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:99.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Xc63C9dBoc5pCXekPMaVDo.jpg" alt="The Man-Machine"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Kraftwerk – The Man-Machine</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121496-a6e3-11f1-93b4-af1280aa57e0">            <a href="https://www.amazon.com/Arca/dp/B06WVPSP92/" data-model-name="Arca – Arca" 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/9YoqEjBbsGuxyprmdXRWd4.jpg" alt="Arca"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Arca – Arca</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121644-a6e3-11f1-9709-61c2f4fb993e">            <a href="https://www.amazon.com/BRAT-Charli-XCX/dp/B0CZ9TWB5K/" data-model-name="Charli xcx – Brat" 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/rHYZn6YmFUNDRBcM9u8NzV.jpg" alt="ATLANTIC RECORDS, Charli xcx – Brat"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Charli xcx – Brat</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b120eba-a6e3-11f1-98bc-a92dec7367c6">            <a href="https://www.amazon.com/Selected-Ambient-Works-85-92/dp/B00E8K2I9Y/" data-model-name="Aphex Twin – Selected Ambient Works 85-92" 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/fogjFPBMx9uVGxJAU87Xkn.jpg" alt="Selected Ambient Works 85-92"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Aphex Twin – Selected Ambient Works 85-92</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121374-a6e3-11f1-a8b6-9fff81cd476c">            <a href="https://www.amazon.com/Dig-Your-Own-Hole-Reissue/dp/B00004WPG5/" data-model-name="Chemical Brothers – Dig Your Own Hole" 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/PaaJLdU4EN86UJQxN3SN74.jpg" alt="Dig Your Own Hole [2 Lp] [reissue]"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Chemical Brothers – Dig Your Own Hole</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b1216d0-a6e3-11f1-8104-b13fac8ebd0b">            <a href="https://www.amazon.com/Homogenic-Vinyl-Bj%C3%B6rk/dp/B00004WSZ2/" data-model-name="Bjork – Homogenic" 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/jcEttQjtykzm6tdCZji8t5.jpg" alt="Homogenic [vinyl]"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Bjork – Homogenic</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b1215b8-a6e3-11f1-973b-330ac1f493d3">            <a href="https://www.amazon.com/MOTHER-EARTHS-PLANTASIA-Mort-Garson/dp/B07QSH9FY8/" data-model-name="Mother Earth's Plantasia" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:97.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rBBTtzeHP3nZFSbwiZUAK5.jpg" alt="Mother Earth's Plantasia"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Mother Earth's Plantasia</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121252-a6e3-11f1-8084-1d24bdfa65e2">            <a href="https://www.amazon.com/Amber-AUTECHRE/dp/B01M08H4WR/" data-model-name="Autechre – Amber" 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/GzSik65pjWbhurMhWaKnp.jpg" alt="Amber"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Autechre – Amber</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b1212e8-a6e3-11f1-8ec3-f169297009fb">            <a href="https://www.amazon.com/Rounds-Reissue-Four-Tet/dp/B00BSWMJ8K/" data-model-name="Four Tet – Rounds" 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/xM3CzVtF5HYkhuLdpHScs3.jpg" alt="Rounds"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Four Tet – Rounds</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b12140a-a6e3-11f1-9c3a-e75bce8a906c">            <a href="https://www.amazon.com/Sound-Silver-Vinyl-LCD-SOUNDSYSTEM/dp/B000M34538/" data-model-name="LCD Soundsystem – Sound of Silver" 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/w5sRKffmHb3yVHXYdk6aP4.jpg" alt="Sound of Silver [vinyl]"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">LCD Soundsystem – Sound of Silver</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b1211c6-a6e3-11f1-8afb-a33caa0f378b">            <a href="https://www.amazon.com/Discovery-Daft-Punk/dp/B099ZRXZ6Q/" data-model-name="Daft Punk. – Discovery" 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/nDoi3zmzBBBRF3Y3CYr2xP.jpg" alt="ADA, Daft Punk. – Discovery"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Daft Punk. – Discovery</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b12152c-a6e3-11f1-819c-0fdd7aaa1a68">            <a href="https://www.amazon.com/Jazz-Frunk-Greats-THROBBING-GRISTLE/dp/B07H62ZCKM/" data-model-name="Throbbing Gristle – 20 Jazz Frunk Greats" 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/wbFsVDWarRQKb7FU6HEw75.jpg" alt="20 Jazz Frunk Greats"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Throbbing Gristle – 20 Jazz Frunk Greats</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b12109a-a6e3-11f1-8a1e-b332a8df3621">            <a href="https://www.amazon.com/Prodigy-Music-Jilted-Generation/dp/B0GKWWDPVR/" data-model-name="The Prodigy – Music for the Jilted Generation" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:103.73%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kEFNB5dSmxUrnmof2A9yUo.jpg" alt="Prodigy - Music for the Jilted Generation"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">The Prodigy – Music for the Jilted Generation</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121004-a6e3-11f1-81b8-df5b45f2a641">            <a href="https://www.amazon.com/Deep-Space-Model-500/dp/B0DL3NPBNS/" data-model-name="Model 500 – Deep Space" 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/jfhf9V7anr8vksVBAAhCCo.jpg" alt="Deep Space"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Model 500 – Deep Space</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1b121130-a6e3-11f1-b318-c9038bcb4f2c">            <a href="https://www.amazon.com/Oil-Every-Pearls-Insides-VINYL/dp/B07HBR6LVS/" data-model-name="Oil of Every Pearl's Un-Insides" 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/NTfRnQFmncNySv5J6GaWD.jpg" alt="Oil of Every Pearl's Un-Insides"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Oil of Every Pearl's Un-Insides</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Bose's Labor Day deals include $160 off one of our favorite headphones — save up to 45% now ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Labor Day weekend is almost here — and in celebration of the holiday, Bose is offering some pretty impressive deals on some of the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best headphones</a> and <a href="https://www.tomsguide.com/best-picks/best-waterproof-speakers">best waterproof Bluetooth speakers</a> on the market. </p><p>If you're in the market for some new gear from the popular audio brand, you're in luck: <a href="https://www.amazon.com/stores/page/80B5AF51-ED3A-4363-BFB2-2F14A871DB51?" target="_blank" rel="nofollow">Amazon is currently knocking up to 45% off Bose devices</a>. Looking for a new speaker? I recommend <a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5/" target="_blank" rel="nofollow">the Bose SoundLink Micro on sale for just $79</a>. When it comes to headphones, you can't go wrong with the <a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0FLYG9C4N?" target="_blank" rel="nofollow">Bose QuietComfort Headphones for $199</a>, which are currently $160 off. </p><p>These rare Bose discounts are bound to disappear soon, so we recommending adding to cart ASAP. Keep scrolling to check out the rest of my favorite picks from the <a href="https://www.tomsguide.com/computing/laptops/labor-day-sales-2026-my-ultimate-guide-to-the-best-end-of-summer-deals">Labor Day sale</a>. </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links </span></h3><ul><li><a href="https://www.amazon.com/stores/page/80B5AF51-ED3A-4363-BFB2-2F14A871DB51?" target="_blank" rel="nofollow"><strong>shop all Bose deals on Amazon</strong></a></li><li><strong>Bose SoundLink Micro: </strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5/" target="_blank" rel="nofollow"><strong>was $129 now $79</strong></a></li><li><strong>Bose SoundLink Flex (Gen 2): </strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0H2B6Q4QN?" target="_blank" rel="nofollow"><strong>was $159 now $129</strong></a></li><li><strong>Bose Ultra Open Earbuds: </strong><a href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><strong>was $299 now $199</strong></a></li><li><strong>Bose QuietComfort Headphones: </strong><a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0FLYG9C4N?" target="_blank" rel="nofollow"><strong>was $349 now $199</strong></a></li><li><strong>Bose SoundLink Max: </strong><a href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow"><strong>was $399 now $329</strong></a></li></ul><h3 class="article-body__section" id="section-best-bose-deals"><span>Best Bose Deals</span></h3><div class="product"><a data-dimension112="352b53f4-a6db-11f1-8507-1fe602824d5d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$79" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1105px;"><p class="vanilla-image-block" style="padding-top:109.50%;"><img id="jBQUxpMSaZYZyyD2LJmGD7" name="Bose Micro" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jBQUxpMSaZYZyyD2LJmGD7.jpg" mos="" align="middle" fullscreen="" width="1105" height="1210" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow" data-dimension112="352b53f4-a6db-11f1-8507-1fe602824d5d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$79">SoundLink Micro Portable Bluetooth Speaker: was $129 now $79</a></strong><br>The SoundLink Micro is a compact, portable speaker you can take on the road. The Bluetooth speaker is waterproof (IPX7 certified) and offers up to 6 hours of play time off a single charge. It can also be paired with another SoundLink speaker to create a stereo mode.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow" data-dimension112="352b53f4-a6db-11f1-8507-1fe602824d5d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="352b5458-a6db-11f1-b9bb-870708329ddf" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$129" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0H2B6Q4QN?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1185px;"><p class="vanilla-image-block" style="padding-top:74.51%;"><img id="v3NbvTNNnQvtyvaC2GVY3L" name="Bose soundflex" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/v3NbvTNNnQvtyvaC2GVY3L.jpg" mos="" align="middle" fullscreen="" width="1185" height="883" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0H2B6Q4QN?" target="_blank" rel="nofollow" data-dimension112="352b5458-a6db-11f1-b9bb-870708329ddf" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$129">SoundLink Flex (Gen 2): was $159 now $129</a></strong><br>The new Bose SoundLink (2nd gen) is a compact and powerful Bluetooth speaker with a durable design. This 1.2-pound speaker is water and dust proof and delivered great sound quality in our <a href="https://www.tomsguide.com/audio/bose-soundlink-flex-2-review">Bose SoundLink Flex gen 2 review</a>. With 12 hours of battery life and $30 off, you have yourself a great deal.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0H2B6Q4QN?" target="_blank" rel="nofollow" data-dimension112="352b5458-a6db-11f1-b9bb-870708329ddf" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="352b54b2-a6db-11f1-9e93-d34b46331a9a" data-action="Deal Block" data-label="SoundLink Plus" data-dimension48="SoundLink Plus" data-dimension25="$199" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0F7HVC62G/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1227px;"><p class="vanilla-image-block" style="padding-top:65.04%;"><img id="9ijfXyWiRHYsywonwsmRwQ" name="soundlink plus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9ijfXyWiRHYsywonwsmRwQ.jpg" mos="" align="middle" fullscreen="" width="1227" height="798" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0F7HVC62G/" target="_blank" rel="nofollow" data-dimension112="352b54b2-a6db-11f1-9e93-d34b46331a9a" data-action="Deal Block" data-label="SoundLink Plus" data-dimension48="SoundLink Plus" data-dimension25="$199">SoundLink Plus: was $269 now $199</a></strong><br>This relatively new Bluetooth speaker is only just seeing its first discounts, and they're really good. This is a big saving on a speaker that offers great sound quality and 20 hours of battery life.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0F7HVC62G/" target="_blank" rel="nofollow" data-dimension112="352b54b2-a6db-11f1-9e93-d34b46331a9a" data-action="Deal Block" data-label="SoundLink Plus" data-dimension48="SoundLink Plus" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="352b5516-a6db-11f1-88b9-61573ffcd3a4" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1268px;"><p class="vanilla-image-block" style="padding-top:62.38%;"><img id="qDBpkjESq4Z3UsvHc4vEeX" name="bose open earbuds" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qDBpkjESq4Z3UsvHc4vEeX.jpg" mos="" align="middle" fullscreen="" width="1268" height="791" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose Ultra Open perfected the open earbud formula. They have a comfortable, non-invasive fit, solid battery life, and they sound better than you might expect from open buds. They got 4 stars in our <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds" data-dimension112="352b5516-a6db-11f1-88b9-61573ffcd3a4" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">Bose Ultra Open Earbuds review</a>, where we loved their simple controls and surprising lack of sound leakage. <a class="view-deal button" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="352b5516-a6db-11f1-88b9-61573ffcd3a4" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="352b55c0-a6db-11f1-9595-fd1f20c51bfa" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$199" href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0FLYG9C4N?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:134.46%;"><img id="hHbg269p5MJKe2LQvoMyMd" name="bose quietcomfort" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/hHbg269p5MJKe2LQvoMyMd.jpg" mos="" align="middle" fullscreen="" width="679" height="913" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In our <a href="https://www.tomsguide.com/reviews/bose-quietcomfort-headphones" data-dimension112="352b55c0-a6db-11f1-9595-fd1f20c51bfa" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$199">Bose QuietComfort Headphones review</a>, we said these are "excellent always-on noise-cancelers with enhanced sound and battery life." They're missing spatial audio mode and touch controls, but for $160 less, these are nearly perfect. <a class="view-deal button" href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0FLYG9C4N?" target="_blank" rel="nofollow" data-dimension112="352b55c0-a6db-11f1-9595-fd1f20c51bfa" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="352b5688-a6db-11f1-b0bc-252ce9f828b7" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$329" href="https://www.amazon.com/dp/B0CVL1K7DX/" 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="dT2cC2J8saDETsZvrBwfqk" name="Bose_SoundLink_Max_square.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/dT2cC2J8saDETsZvrBwfqk.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>With big sound and a fashionable look, the SoundLink Max is a great portable companion for taking on the go. 20 hours of battery life is a big bonus as well, giving you plenty of listening time. <a class="view-deal button" href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow" data-dimension112="352b5688-a6db-11f1-b0bc-252ce9f828b7" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$329">View Deal</a></p></div><div class="product"><a data-dimension112="352b5624-a6db-11f1-8465-c9c5234a8be3" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$329" href="https://www.amazon.com/dp/B0DX2LNZ68?" 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="g64SDV3bmD2tcgopiiNgP6" name="Bose QuietComfort Ultra headphones Versus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/g64SDV3bmD2tcgopiiNgP6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Headphones get you epic ANC, super sound, and the most comfortable fit this side of wrapping your head in a snood that plays music. 24 hours of battery life isn't the best, but they make up for it with just about everything else.<a class="view-deal button" href="https://www.amazon.com/dp/B0DX2LNZ68?" target="_blank" rel="nofollow" data-dimension112="352b5624-a6db-11f1-8465-c9c5234a8be3" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$329">View Deal</a></p></div><div class="product"><a data-dimension112="352b56ec-a6db-11f1-be55-1b69ee569116" data-action="Deal Block" data-label="Smart Ultra Soundbar" data-dimension48="Smart Ultra Soundbar" data-dimension25="$679" href="https://www.amazon.com/Bose-Soundbar-Wireless-Bluetooth-Surround/dp/B0C548MYF3" 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="dLLYXWpy2mJsCzXj262Tq6" name="bose smart soundbar ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/dLLYXWpy2mJsCzXj262Tq6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This discount brings the Bose Smart Ultra soundbar to its lowest ever price right now. This premium soundbar has a 5.1.2 channel setup and works with Bose's subwoofer range (sold separately). One of the best parts of the Bose Smart range links up with music streaming apps for seamless listening!<a class="view-deal button" href="https://www.amazon.com/Bose-Soundbar-Wireless-Bluetooth-Surround/dp/B0C548MYF3" target="_blank" rel="nofollow" data-dimension112="352b56ec-a6db-11f1-be55-1b69ee569116" data-action="Deal Block" data-label="Smart Ultra Soundbar" data-dimension48="Smart Ultra Soundbar" data-dimension25="$679">View Deal</a></p></div><h3 class="article-body__section" id="section-shop-more-deals"><span>Shop More Deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Headphones&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/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/audio/boses-labor-day-deals-include-usd160-off-one-of-our-favorite-headphones-save-up-to-45-percent-now</link>
                                                                            <description>
                            <![CDATA[ Amazon is knocking up to 45% off Bose speakers and headphones. Check out my favorite picks from the sale. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">frV6tGoVuQJtL4cRumQKUB</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ufg7vA4cVQQ5qaK6d7SrrJ-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 02 Sep 2026 15:15:40 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Olivia Halevy ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/YAg7Fmh9dGKbg3hULBMWiU.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Olivia is a Deals Writer at Tom’s Guide. She loves all things lifestyle, shopping and sourcing the best deals from some of the top brands, retailers and websites around. From fashion and beauty to smart home and tech, she has an eye for finding practical, stylish, and reasonably-priced items that help make life simpler (and a little more aesthetically-pleasing).&amp;nbsp;Olivia has over five years of experience writing lifestyle, commerce, and branded content for &lt;a href=&quot;http://patch.com/&quot;&gt;Patch.com&lt;/a&gt;. She&#039;s covered major shopping holidays like Amazon Prime Day, Black Friday and Cyber Monday. In her free time, she enjoys trying out new exercise classes, binge-watching the latest viral TV shows, cooking, spending time with family and taking long walks with her dog, Gus.&amp;nbsp;&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/ufg7vA4cVQQ5qaK6d7SrrJ-1280-80.jpg">
                                                            <media:credit><![CDATA[Amazon / edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Bose]]></media:description>                                                            <media:text><![CDATA[Bose]]></media:text>
                                <media:title type="plain"><![CDATA[Bose]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ufg7vA4cVQQ5qaK6d7SrrJ-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Labor Day weekend is almost here — and in celebration of the holiday, Bose is offering some pretty impressive deals on some of the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best headphones</a> and <a href="https://www.tomsguide.com/best-picks/best-waterproof-speakers">best waterproof Bluetooth speakers</a> on the market. </p><p>If you're in the market for some new gear from the popular audio brand, you're in luck: <a href="https://www.amazon.com/stores/page/80B5AF51-ED3A-4363-BFB2-2F14A871DB51?" target="_blank" rel="nofollow">Amazon is currently knocking up to 45% off Bose devices</a>. Looking for a new speaker? I recommend <a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5/" target="_blank" rel="nofollow">the Bose SoundLink Micro on sale for just $79</a>. When it comes to headphones, you can't go wrong with the <a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0FLYG9C4N?" target="_blank" rel="nofollow">Bose QuietComfort Headphones for $199</a>, which are currently $160 off. </p><p>These rare Bose discounts are bound to disappear soon, so we recommending adding to cart ASAP. Keep scrolling to check out the rest of my favorite picks from the <a href="https://www.tomsguide.com/computing/laptops/labor-day-sales-2026-my-ultimate-guide-to-the-best-end-of-summer-deals">Labor Day sale</a>. </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links </span></h3><ul><li><a href="https://www.amazon.com/stores/page/80B5AF51-ED3A-4363-BFB2-2F14A871DB51?" target="_blank" rel="nofollow"><strong>shop all Bose deals on Amazon</strong></a></li><li><strong>Bose SoundLink Micro: </strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5/" target="_blank" rel="nofollow"><strong>was $129 now $79</strong></a></li><li><strong>Bose SoundLink Flex (Gen 2): </strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0H2B6Q4QN?" target="_blank" rel="nofollow"><strong>was $159 now $129</strong></a></li><li><strong>Bose Ultra Open Earbuds: </strong><a href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><strong>was $299 now $199</strong></a></li><li><strong>Bose QuietComfort Headphones: </strong><a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0FLYG9C4N?" target="_blank" rel="nofollow"><strong>was $349 now $199</strong></a></li><li><strong>Bose SoundLink Max: </strong><a href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow"><strong>was $399 now $329</strong></a></li></ul><h3 class="article-body__section" id="section-best-bose-deals"><span>Best Bose Deals</span></h3><div class="product"><a data-dimension112="352b53f4-a6db-11f1-8507-1fe602824d5d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$79" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1105px;"><p class="vanilla-image-block" style="padding-top:109.50%;"><img id="jBQUxpMSaZYZyyD2LJmGD7" name="Bose Micro" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jBQUxpMSaZYZyyD2LJmGD7.jpg" mos="" align="middle" fullscreen="" width="1105" height="1210" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow" data-dimension112="352b53f4-a6db-11f1-8507-1fe602824d5d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$79">SoundLink Micro Portable Bluetooth Speaker: was $129 now $79</a></strong><br>The SoundLink Micro is a compact, portable speaker you can take on the road. The Bluetooth speaker is waterproof (IPX7 certified) and offers up to 6 hours of play time off a single charge. It can also be paired with another SoundLink speaker to create a stereo mode.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow" data-dimension112="352b53f4-a6db-11f1-8507-1fe602824d5d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$79">View Deal</a></p></div><div class="product"><a data-dimension112="352b5458-a6db-11f1-b9bb-870708329ddf" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$129" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0H2B6Q4QN?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1185px;"><p class="vanilla-image-block" style="padding-top:74.51%;"><img id="v3NbvTNNnQvtyvaC2GVY3L" name="Bose soundflex" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/v3NbvTNNnQvtyvaC2GVY3L.jpg" mos="" align="middle" fullscreen="" width="1185" height="883" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0H2B6Q4QN?" target="_blank" rel="nofollow" data-dimension112="352b5458-a6db-11f1-b9bb-870708329ddf" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$129">SoundLink Flex (Gen 2): was $159 now $129</a></strong><br>The new Bose SoundLink (2nd gen) is a compact and powerful Bluetooth speaker with a durable design. This 1.2-pound speaker is water and dust proof and delivered great sound quality in our <a href="https://www.tomsguide.com/audio/bose-soundlink-flex-2-review">Bose SoundLink Flex gen 2 review</a>. With 12 hours of battery life and $30 off, you have yourself a great deal.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0H2B6Q4QN?" target="_blank" rel="nofollow" data-dimension112="352b5458-a6db-11f1-b9bb-870708329ddf" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="352b54b2-a6db-11f1-9e93-d34b46331a9a" data-action="Deal Block" data-label="SoundLink Plus" data-dimension48="SoundLink Plus" data-dimension25="$199" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0F7HVC62G/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1227px;"><p class="vanilla-image-block" style="padding-top:65.04%;"><img id="9ijfXyWiRHYsywonwsmRwQ" name="soundlink plus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9ijfXyWiRHYsywonwsmRwQ.jpg" mos="" align="middle" fullscreen="" width="1227" height="798" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0F7HVC62G/" target="_blank" rel="nofollow" data-dimension112="352b54b2-a6db-11f1-9e93-d34b46331a9a" data-action="Deal Block" data-label="SoundLink Plus" data-dimension48="SoundLink Plus" data-dimension25="$199">SoundLink Plus: was $269 now $199</a></strong><br>This relatively new Bluetooth speaker is only just seeing its first discounts, and they're really good. This is a big saving on a speaker that offers great sound quality and 20 hours of battery life.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0F7HVC62G/" target="_blank" rel="nofollow" data-dimension112="352b54b2-a6db-11f1-9e93-d34b46331a9a" data-action="Deal Block" data-label="SoundLink Plus" data-dimension48="SoundLink Plus" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="352b5516-a6db-11f1-88b9-61573ffcd3a4" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1268px;"><p class="vanilla-image-block" style="padding-top:62.38%;"><img id="qDBpkjESq4Z3UsvHc4vEeX" name="bose open earbuds" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qDBpkjESq4Z3UsvHc4vEeX.jpg" mos="" align="middle" fullscreen="" width="1268" height="791" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose Ultra Open perfected the open earbud formula. They have a comfortable, non-invasive fit, solid battery life, and they sound better than you might expect from open buds. They got 4 stars in our <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds" data-dimension112="352b5516-a6db-11f1-88b9-61573ffcd3a4" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">Bose Ultra Open Earbuds review</a>, where we loved their simple controls and surprising lack of sound leakage. <a class="view-deal button" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="352b5516-a6db-11f1-88b9-61573ffcd3a4" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="352b55c0-a6db-11f1-9595-fd1f20c51bfa" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$199" href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0FLYG9C4N?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:134.46%;"><img id="hHbg269p5MJKe2LQvoMyMd" name="bose quietcomfort" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/hHbg269p5MJKe2LQvoMyMd.jpg" mos="" align="middle" fullscreen="" width="679" height="913" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In our <a href="https://www.tomsguide.com/reviews/bose-quietcomfort-headphones" data-dimension112="352b55c0-a6db-11f1-9595-fd1f20c51bfa" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$199">Bose QuietComfort Headphones review</a>, we said these are "excellent always-on noise-cancelers with enhanced sound and battery life." They're missing spatial audio mode and touch controls, but for $160 less, these are nearly perfect. <a class="view-deal button" href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0FLYG9C4N?" target="_blank" rel="nofollow" data-dimension112="352b55c0-a6db-11f1-9595-fd1f20c51bfa" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="352b5688-a6db-11f1-b0bc-252ce9f828b7" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$329" href="https://www.amazon.com/dp/B0CVL1K7DX/" 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="dT2cC2J8saDETsZvrBwfqk" name="Bose_SoundLink_Max_square.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/dT2cC2J8saDETsZvrBwfqk.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>With big sound and a fashionable look, the SoundLink Max is a great portable companion for taking on the go. 20 hours of battery life is a big bonus as well, giving you plenty of listening time. <a class="view-deal button" href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow" data-dimension112="352b5688-a6db-11f1-b0bc-252ce9f828b7" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$329">View Deal</a></p></div><div class="product"><a data-dimension112="352b5624-a6db-11f1-8465-c9c5234a8be3" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$329" href="https://www.amazon.com/dp/B0DX2LNZ68?" 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="g64SDV3bmD2tcgopiiNgP6" name="Bose QuietComfort Ultra headphones Versus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/g64SDV3bmD2tcgopiiNgP6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Headphones get you epic ANC, super sound, and the most comfortable fit this side of wrapping your head in a snood that plays music. 24 hours of battery life isn't the best, but they make up for it with just about everything else.<a class="view-deal button" href="https://www.amazon.com/dp/B0DX2LNZ68?" target="_blank" rel="nofollow" data-dimension112="352b5624-a6db-11f1-8465-c9c5234a8be3" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$329">View Deal</a></p></div><div class="product"><a data-dimension112="352b56ec-a6db-11f1-be55-1b69ee569116" data-action="Deal Block" data-label="Smart Ultra Soundbar" data-dimension48="Smart Ultra Soundbar" data-dimension25="$679" href="https://www.amazon.com/Bose-Soundbar-Wireless-Bluetooth-Surround/dp/B0C548MYF3" 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="dLLYXWpy2mJsCzXj262Tq6" name="bose smart soundbar ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/dLLYXWpy2mJsCzXj262Tq6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This discount brings the Bose Smart Ultra soundbar to its lowest ever price right now. This premium soundbar has a 5.1.2 channel setup and works with Bose's subwoofer range (sold separately). One of the best parts of the Bose Smart range links up with music streaming apps for seamless listening!<a class="view-deal button" href="https://www.amazon.com/Bose-Soundbar-Wireless-Bluetooth-Surround/dp/B0C548MYF3" target="_blank" rel="nofollow" data-dimension112="352b56ec-a6db-11f1-be55-1b69ee569116" data-action="Deal Block" data-label="Smart Ultra Soundbar" data-dimension48="Smart Ultra Soundbar" data-dimension25="$679">View Deal</a></p></div><h3 class="article-body__section" id="section-shop-more-deals"><span>Shop More Deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Headphones&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = this.getRowsLimit();          } else {             this.displayLimit += this.getRowsLimit();          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += this.getRowsLimit();                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');               document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tried Sonos Ace Ultra, and it does something your Bluetooth headphones can’t ]]></title>
                                                                                                <dc:content><![CDATA[ <p>It’s easy to look at the new $449 Sonos Ace Ultra and wonder why anyone needs living room headphones when standard TV Bluetooth and other playback hand-off options already exist.</p><p>I’ll take it one step further. I’ve historically been skeptical of "at-home" headphones. I don't have kids sleeping down the hall or roommates to tip-toe around during a late-night movie binge, so headphones have always felt like a tool for travel, not my couch.</p><p>But after trying out the headset alongside the <a href="https://www.tomsguide.com/audio/soundbars/sonos-is-ready-to-steal-back-the-soundbar-crown-with-the-new-beam-ultra">Beam Ultra</a> in a living room setting, I believe it reveals a fundamental shift in flexible listening. The Ace Ultra bypasses traditional Bluetooth constraints entirely to operate in respect to the rest of a Wi-Fi-connected, whole-home audio system. </p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7680646449184427278" data-video-id="7680646449184427278" 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="♬ Slow Dial Rotation - Margot Floodlight" href="https://www.tiktok.com/music/Slow-Dial-Rotation-7679904643178088498">♬ Slow Dial Rotation - Margot Floodlight</a></section>                    </blockquote></div>                <h2 id="no-your-bluetooth-headphones-can-t-do-this">No, your Bluetooth headphones can’t do this</h2><p>Connecting standard <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">Bluetooth headphones</a> to a smart TV comes with major trade-offs, including potential lip-sync latency, compressed audio, limited range and the general annoyance of digging through settings menus when you want to switch back to your TV speakers.</p><p>The Ace Ultra takes a different approach. With dedicated Headphone Engine 2 silicon, it can connect to your local Wi-Fi network rather than relying solely on a Bluetooth connection to your phone or TV. This enables uncompressed, zero-latency spatial audio. </p><div class="product"><a data-dimension112="7bba94d2-a63c-11f1-8b9b-714b82fecc21" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449" href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:536px;"><p class="vanilla-image-block" style="padding-top:108.58%;"><img id="jEBPbWV6VERCE8peQaX9Xb" name="Ace Ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jEBPbWV6VERCE8peQaX9Xb.png" mos="" align="middle" fullscreen="" width="536" height="582" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow" data-dimension112="7bba94d2-a63c-11f1-8b9b-714b82fecc21" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449">Ace Ultra: $449</a></strong><br>Noise-canceling headphones with rich, detailed audio, up to 35 hours of battery with ANC on, and a direct connection to your Sonos system.<a class="view-deal button" href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow" data-dimension112="7bba94d2-a63c-11f1-8b9b-714b82fecc21" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449">View Deal</a></p></div><p>And instead of applying a generic virtual surround filter, TrueCinema room calibration uses living room spatial data to map audio to the exact physical acoustics of your room. Because it streams over Wi-Fi, you can walk throughout your entire house without audio stutters or range limits. This made the Ace Ultra feel less like a pair of headphones connected to a TV and more like another endpoint within the Sonos network.</p><h2 id="no-the-original-sonos-ace-also-couldn-t-do-this">No, the original Sonos Ace also couldn’t do this</h2><p>While it’s true that the original <a href="https://www.tomsguide.com/audio/over-ear-headphones/sonos-ace">Sonos Ace</a> released in 2024 introduced TV Audio Swap, that feature was strictly a point-to-point link locked to a single soundbar. The Ace Ultra breaks out of that single-room boundary through the broader architecture of <a href="https://www.tomsguide.com/audio/speakers/sonos-27-is-here-to-clean-up-the-app-mess-again-this-time-with-ai">Sonos 27, </a>what Sonos is calling the latest software version of its system.</p><p>Through new Headphone Linking capabilities, the headset interacts directly with your whole home setup. Walking up to virtually any Sonos speaker in your house and tapping the physical action button on the earcup instantly pushes or pulls whatever audio is playing in that room. </p><p>The TV does not need to be on, an app does not need to be opened and your phone can be completely turned off in another room.</p><p>I think Apple has the closest version of this kind of hand-off implementation between its <a href="https://www.tomsguide.com/reviews/apple-homepod-2nd-generation">Homepod</a> speakers and mobile devices, but again, you still need to physically tap your iPhone to the speaker to initiate the switch even if using AirPlay. You also cannot directly hand off audio from a HomePod to AirPods with a button press on the headphones themselves. Sonos is doing something genuinely distinct by making the headset an independent network node.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2497px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="NtNJBviKW6PWJEDQzucgng" name="dji_mimo_20260901_112012_20260901112013_1788277197059_photo" alt="Sonos Ace Ultra on head" src="https://cdn.mos.cms.futurecdn.net/NtNJBviKW6PWJEDQzucgng.jpg" mos="" align="middle" fullscreen="" width="2497" height="1405" 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><h2 id="yes-you-can-use-them-outside-of-your-house-too">Yes, you can use them outside of your house, too</h2><p>Even if you think you’ll rarely use the direct system-linking in your living room, the Ace Ultra makes a strong case as a travel-first flagship. In a loud event space packed with overlapping audio demos, the physical hardware excelled where traditional commuters care most.</p><p>Powered by a 10-microphone array, the noise cancellation cuts through heavy ambient room noise while maintaining clean separation between vocal and instrumental layers.</p><p>Sonos re-engineered the custom 44mm driver with a lighter, stiffer diaphragm, optimizing the power-to-weight ratio to deliver crisp dialogue, broad dynamic range and deeper bass.</p><p>You get up to 35 hours of battery life with ANC enabled. And beyond Wi-Fi linking, the headset offers Bluetooth, USB-C and 3.5mm wired options, finished in new matte colors.</p><p>At $449, the Ace Ultra delivers the acoustic isolation, long-haul comfort and battery life required to go head-to-head with travel staples from Sony and Bose. </p><p>But by removing the phone as the middleman and turning the Ace Ultra into a physical extension of your home network, Sonos is making a compelling case for keeping its headphones on when you walk back in through your home’s front door.</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/soundbars/sonos-is-ready-to-steal-back-the-soundbar-crown-with-the-new-beam-ultra">Sonos is ready to steal back the soundbar crown with the new Beam Ultra</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/i-just-set-up-the-ultimate-sonos-surround-system-despite-some-connection-foibles-its-here-to-stay">I just set up the ultimate Sonos surround system</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/change-these-sonos-soundbar-settings-immediately-to-get-the-best-performance-out-of-your-tv-and-movie-watching">Change these Sonos soundbar settings immediately</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/headphones/i-tried-sonos-ace-ultra-and-it-does-something-your-bluetooth-headphones-cant</link>
                                                                            <description>
                            <![CDATA[ The Sonos Ace Ultra’s standout feature is how connects to your entire Sonos system over Wi-Fi and lets you seamlessly move audio around your home. I went hands-on with the Ace Ultra to learn more about how it works. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">9Mp3jYoV5NdaRqjSA2DTNY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/g37jScEfoAqx9dZZQn3sYC-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 01 Sep 2026 21:04:01 +0000</pubDate>                                                                                                                                <updated>Tue, 01 Sep 2026 23:11:29 +0000</updated>
                                                                                                                                            <category><![CDATA[Headphones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Kate Kozuch ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/xAVUdx6Qtp3SzugnnfNYsL.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/g37jScEfoAqx9dZZQn3sYC-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sonos Ace Ultra connected to Sonos Beam Ultra soundbar underneath TV]]></media:description>                                                            <media:text><![CDATA[Sonos Ace Ultra connected to Sonos Beam Ultra soundbar underneath TV]]></media:text>
                                <media:title type="plain"><![CDATA[Sonos Ace Ultra connected to Sonos Beam Ultra soundbar underneath TV]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/g37jScEfoAqx9dZZQn3sYC-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It’s easy to look at the new $449 Sonos Ace Ultra and wonder why anyone needs living room headphones when standard TV Bluetooth and other playback hand-off options already exist.</p><p>I’ll take it one step further. I’ve historically been skeptical of "at-home" headphones. I don't have kids sleeping down the hall or roommates to tip-toe around during a late-night movie binge, so headphones have always felt like a tool for travel, not my couch.</p><p>But after trying out the headset alongside the <a href="https://www.tomsguide.com/audio/soundbars/sonos-is-ready-to-steal-back-the-soundbar-crown-with-the-new-beam-ultra">Beam Ultra</a> in a living room setting, I believe it reveals a fundamental shift in flexible listening. The Ace Ultra bypasses traditional Bluetooth constraints entirely to operate in respect to the rest of a Wi-Fi-connected, whole-home audio system. </p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7680646449184427278" data-video-id="7680646449184427278" 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="♬ Slow Dial Rotation - Margot Floodlight" href="https://www.tiktok.com/music/Slow-Dial-Rotation-7679904643178088498">♬ Slow Dial Rotation - Margot Floodlight</a></section>                    </blockquote></div>                <h2 id="no-your-bluetooth-headphones-can-t-do-this">No, your Bluetooth headphones can’t do this</h2><p>Connecting standard <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">Bluetooth headphones</a> to a smart TV comes with major trade-offs, including potential lip-sync latency, compressed audio, limited range and the general annoyance of digging through settings menus when you want to switch back to your TV speakers.</p><p>The Ace Ultra takes a different approach. With dedicated Headphone Engine 2 silicon, it can connect to your local Wi-Fi network rather than relying solely on a Bluetooth connection to your phone or TV. This enables uncompressed, zero-latency spatial audio. </p><div class="product"><a data-dimension112="7bba94d2-a63c-11f1-8b9b-714b82fecc21" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449" href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:536px;"><p class="vanilla-image-block" style="padding-top:108.58%;"><img id="jEBPbWV6VERCE8peQaX9Xb" name="Ace Ultra" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jEBPbWV6VERCE8peQaX9Xb.png" mos="" align="middle" fullscreen="" width="536" height="582" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow" data-dimension112="7bba94d2-a63c-11f1-8b9b-714b82fecc21" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449">Ace Ultra: $449</a></strong><br>Noise-canceling headphones with rich, detailed audio, up to 35 hours of battery with ANC on, and a direct connection to your Sonos system.<a class="view-deal button" href="https://www.sonos.com/en-us/shop/sonos-ace-ultra" target="_blank" rel="nofollow" data-dimension112="7bba94d2-a63c-11f1-8b9b-714b82fecc21" data-action="Deal Block" data-label="Ace Ultra" data-dimension48="Ace Ultra" data-dimension25="$449">View Deal</a></p></div><p>And instead of applying a generic virtual surround filter, TrueCinema room calibration uses living room spatial data to map audio to the exact physical acoustics of your room. Because it streams over Wi-Fi, you can walk throughout your entire house without audio stutters or range limits. This made the Ace Ultra feel less like a pair of headphones connected to a TV and more like another endpoint within the Sonos network.</p><h2 id="no-the-original-sonos-ace-also-couldn-t-do-this">No, the original Sonos Ace also couldn’t do this</h2><p>While it’s true that the original <a href="https://www.tomsguide.com/audio/over-ear-headphones/sonos-ace">Sonos Ace</a> released in 2024 introduced TV Audio Swap, that feature was strictly a point-to-point link locked to a single soundbar. The Ace Ultra breaks out of that single-room boundary through the broader architecture of <a href="https://www.tomsguide.com/audio/speakers/sonos-27-is-here-to-clean-up-the-app-mess-again-this-time-with-ai">Sonos 27, </a>what Sonos is calling the latest software version of its system.</p><p>Through new Headphone Linking capabilities, the headset interacts directly with your whole home setup. Walking up to virtually any Sonos speaker in your house and tapping the physical action button on the earcup instantly pushes or pulls whatever audio is playing in that room. </p><p>The TV does not need to be on, an app does not need to be opened and your phone can be completely turned off in another room.</p><p>I think Apple has the closest version of this kind of hand-off implementation between its <a href="https://www.tomsguide.com/reviews/apple-homepod-2nd-generation">Homepod</a> speakers and mobile devices, but again, you still need to physically tap your iPhone to the speaker to initiate the switch even if using AirPlay. You also cannot directly hand off audio from a HomePod to AirPods with a button press on the headphones themselves. Sonos is doing something genuinely distinct by making the headset an independent network node.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2497px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="NtNJBviKW6PWJEDQzucgng" name="dji_mimo_20260901_112012_20260901112013_1788277197059_photo" alt="Sonos Ace Ultra on head" src="https://cdn.mos.cms.futurecdn.net/NtNJBviKW6PWJEDQzucgng.jpg" mos="" align="middle" fullscreen="" width="2497" height="1405" 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><h2 id="yes-you-can-use-them-outside-of-your-house-too">Yes, you can use them outside of your house, too</h2><p>Even if you think you’ll rarely use the direct system-linking in your living room, the Ace Ultra makes a strong case as a travel-first flagship. In a loud event space packed with overlapping audio demos, the physical hardware excelled where traditional commuters care most.</p><p>Powered by a 10-microphone array, the noise cancellation cuts through heavy ambient room noise while maintaining clean separation between vocal and instrumental layers.</p><p>Sonos re-engineered the custom 44mm driver with a lighter, stiffer diaphragm, optimizing the power-to-weight ratio to deliver crisp dialogue, broad dynamic range and deeper bass.</p><p>You get up to 35 hours of battery life with ANC enabled. And beyond Wi-Fi linking, the headset offers Bluetooth, USB-C and 3.5mm wired options, finished in new matte colors.</p><p>At $449, the Ace Ultra delivers the acoustic isolation, long-haul comfort and battery life required to go head-to-head with travel staples from Sony and Bose. </p><p>But by removing the phone as the middleman and turning the Ace Ultra into a physical extension of your home network, Sonos is making a compelling case for keeping its headphones on when you walk back in through your home’s front door.</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/soundbars/sonos-is-ready-to-steal-back-the-soundbar-crown-with-the-new-beam-ultra">Sonos is ready to steal back the soundbar crown with the new Beam Ultra</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/i-just-set-up-the-ultimate-sonos-surround-system-despite-some-connection-foibles-its-here-to-stay">I just set up the ultimate Sonos surround system</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/change-these-sonos-soundbar-settings-immediately-to-get-the-best-performance-out-of-your-tv-and-movie-watching">Change these Sonos soundbar settings immediately</a></li></ul>
                                                            </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.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/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.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.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.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.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-1280-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.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-1280-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-1280-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.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/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.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.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.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.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[ Sonos is ready to steal back the soundbar crown with the new Beam Ultra ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Sonos is no stranger to the world of soundbars. The <a href="https://www.tomsguide.com/audio/soundbars/sonos-arc-ultra-review">Sonos Arc Ultra</a> is widely regarded as one of the <a href="https://www.tomsguide.com/audio/soundbars/best-soundbars">best soundbars</a> on the market — but its $1,100 sticker price makes it a tough sell when wallets are stretched thin. </p><p>That’s where the new Sonos Beam Ultra coms in. Announced early at an event in New York City on September 1, the new mid-range soundbar sports two dedicated up-firing drivers and a redesigned center channel that deliver clearer dialogue.</p><p>From everything I've seen on it, the Beam Ultra is a compact 7.1.2 Dolby Atmos soundbar that fits everything I love about its more-expensive sibling into a $700 price point. Here’s what makes it the soundbar to watch when it becomes available on September 29.</p><h2 id="big-sound-small-package">Big sound, small package</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1490px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="oRmpa3Rxs8pF6uqDRfdU7X" name="Beam Ultra 8.JPG" alt="A black Sonos Beam Ultra on an entertainment stand." src="https://cdn.mos.cms.futurecdn.net/oRmpa3Rxs8pF6uqDRfdU7X.jpg" mos="" align="middle" fullscreen="" width="1490" height="838" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sonos)</span></figcaption></figure><p>The current-generation <a href="https://www.tomsguide.com/reviews/sonos-beam-gen-2">Sonos Beam (2nd Gen)</a> does a great job with its 5.0-channel configuration — but the Beam Ultra uses nine speakers, including the two additional up-firing drivers, that are going to make a world of difference for surround sound movie soundtracks. </p><p>While the new Beam Ultra won’t ship with any satellite speakers or subwoofers, the Beam Ultra is compatible with the whole Sonos ecosystem including, of course, the Sonos Sub and <a href="https://www.tomsguide.com/reviews/sonos-sub-mini">Sonos Sub Mini</a> that can be used for a meatier low-end. </p><p>Sonos says the newly designed center channel driver enables clearer dialogue and can tap into four levels of AI Speech Enhancement for even more pronounced mids, as well as a Night Sound mode when you want to cut back on any wall-rattling frequencies.</p><p>Last but not least, Sonos says the new Beam Ultra will support the brand’s Trueplay technology that takes an aural image of the room and adjusts the output of the sound accordingly. It’s a handy feature, though one that never quite matches the effects of proper sound dampening in my experience.</p><h2 id="the-beam-ultra-offers-both-brains-and-brawn">The Beam Ultra offers both brains and brawn</h2><p>In addition to its revamped internals, the Sonos Beam Ultra will leverage the company’s new Sonos 27 smart platform. Those words might scare you considering that the last time the company launched a new smart platform, it went so poorly that it <a href="https://www.tomsguide.com/audio/speakers/sonos-ceo-exits-following-major-app-fail-but-theres-good-news">cost the CEO his job</a> — but Sonos 27 feels like it could actually make the platform better overall. </p><p>Key features in the latest platform are Sonos 27voice and Sonos 27mcp. The former is Sonos’ new voice assistant that will live (I assume) alongside Alexa, while the latter allows you to connect your Sonos ecosystem to any outside AI assistant or agent.</p><p>Eventually, Sonos will allow users to design their own custom agents — with a matching persona and voice — but details on that are relatively slim at the moment. </p><div><blockquote><p>Eventually, Sonos will allow users to design their own custom agents — with a matching persona and voice</p></blockquote></div><p>Finally, anyone who uses Sonos portable speakers like the <a href="https://www.tomsguide.com/reviews/sonos-move-2">Move 2</a> or <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> will be able to use them in a future update as surround sound speakers for the Beam Ultra. This feature, dubbed Sonos Fabric, uses high-frequency sound that enables Sonos products to “sense each other” and “adapt to the moment.” </p><p>We'll certainly test that ourselves when the feature becomes available at some point in the future.</p><h2 id="sony-eat-your-heart-out">Sony, eat your heart out</h2><p>Sonos isn't the only one going after the more modest soundbar market — this is Sony, Samsung and LG's bread and butter. </p><p>Each year, the trio of TV-makers deliver a half dozen of sub-$1,000 soundbars into the market, and every year they get gobbled up during sales events like Black Friday and, to a lesser extent, Super Bowl Sunday. </p><p>On paper, the Sonos Beam Ultra delivers a lot of value — potentially a whole lot more than what the competition offers — but $700 is still a pretty hefty chunk of change. </p><p>Is it worth the scratch? We'll know more after we hear it for ourselves at the New York event and bring it into our own homes for comparison testing sometime around its September 29 launch date.</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/noble-audio-iris-iems-review">I tested Noble's high-end IEMs and they boast an unbelievably wide soundstage — but are the Iris worth $699?</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/sony-bravia-theater-bar-7-review">I tested the Sony Bravia Theater Bar 7 + subwoofer — and recommending it is a no-brainer</a></li><li><a href="https://www.tomsguide.com/audio/speakers/before-you-buy-your-next-pair-of-speakers-make-sure-you-know-this-one-essential-difference">Before you buy your next pair of speakers, make sure you know this one essential difference</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/soundbars/sonos-is-ready-to-steal-back-the-soundbar-crown-with-the-new-beam-ultra</link>
                                                                            <description>
                            <![CDATA[ Time to upgrade your soundbar? The all-new Sonos Beam Ultra delivers 7.1.2 Dolby Atmos sound for under $700. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">542a3j8jzGJ78F9xBKfrdQ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/LprZFdxoeDbSM5yL2azuYP-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 01 Sep 2026 14:40:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Soundbars]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Speakers]]></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.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/LprZFdxoeDbSM5yL2azuYP-1280-80.jpg">
                                                            <media:credit><![CDATA[Sonos]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The new Sonos Beam Ultra on an entertainment center.]]></media:description>                                                            <media:text><![CDATA[The new Sonos Beam Ultra on an entertainment center.]]></media:text>
                                <media:title type="plain"><![CDATA[The new Sonos Beam Ultra on an entertainment center.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/LprZFdxoeDbSM5yL2azuYP-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Sonos is no stranger to the world of soundbars. The <a href="https://www.tomsguide.com/audio/soundbars/sonos-arc-ultra-review">Sonos Arc Ultra</a> is widely regarded as one of the <a href="https://www.tomsguide.com/audio/soundbars/best-soundbars">best soundbars</a> on the market — but its $1,100 sticker price makes it a tough sell when wallets are stretched thin. </p><p>That’s where the new Sonos Beam Ultra coms in. Announced early at an event in New York City on September 1, the new mid-range soundbar sports two dedicated up-firing drivers and a redesigned center channel that deliver clearer dialogue.</p><p>From everything I've seen on it, the Beam Ultra is a compact 7.1.2 Dolby Atmos soundbar that fits everything I love about its more-expensive sibling into a $700 price point. Here’s what makes it the soundbar to watch when it becomes available on September 29.</p><h2 id="big-sound-small-package">Big sound, small package</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1490px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="oRmpa3Rxs8pF6uqDRfdU7X" name="Beam Ultra 8.JPG" alt="A black Sonos Beam Ultra on an entertainment stand." src="https://cdn.mos.cms.futurecdn.net/oRmpa3Rxs8pF6uqDRfdU7X.jpg" mos="" align="middle" fullscreen="" width="1490" height="838" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sonos)</span></figcaption></figure><p>The current-generation <a href="https://www.tomsguide.com/reviews/sonos-beam-gen-2">Sonos Beam (2nd Gen)</a> does a great job with its 5.0-channel configuration — but the Beam Ultra uses nine speakers, including the two additional up-firing drivers, that are going to make a world of difference for surround sound movie soundtracks. </p><p>While the new Beam Ultra won’t ship with any satellite speakers or subwoofers, the Beam Ultra is compatible with the whole Sonos ecosystem including, of course, the Sonos Sub and <a href="https://www.tomsguide.com/reviews/sonos-sub-mini">Sonos Sub Mini</a> that can be used for a meatier low-end. </p><p>Sonos says the newly designed center channel driver enables clearer dialogue and can tap into four levels of AI Speech Enhancement for even more pronounced mids, as well as a Night Sound mode when you want to cut back on any wall-rattling frequencies.</p><p>Last but not least, Sonos says the new Beam Ultra will support the brand’s Trueplay technology that takes an aural image of the room and adjusts the output of the sound accordingly. It’s a handy feature, though one that never quite matches the effects of proper sound dampening in my experience.</p><h2 id="the-beam-ultra-offers-both-brains-and-brawn">The Beam Ultra offers both brains and brawn</h2><p>In addition to its revamped internals, the Sonos Beam Ultra will leverage the company’s new Sonos 27 smart platform. Those words might scare you considering that the last time the company launched a new smart platform, it went so poorly that it <a href="https://www.tomsguide.com/audio/speakers/sonos-ceo-exits-following-major-app-fail-but-theres-good-news">cost the CEO his job</a> — but Sonos 27 feels like it could actually make the platform better overall. </p><p>Key features in the latest platform are Sonos 27voice and Sonos 27mcp. The former is Sonos’ new voice assistant that will live (I assume) alongside Alexa, while the latter allows you to connect your Sonos ecosystem to any outside AI assistant or agent.</p><p>Eventually, Sonos will allow users to design their own custom agents — with a matching persona and voice — but details on that are relatively slim at the moment. </p><div><blockquote><p>Eventually, Sonos will allow users to design their own custom agents — with a matching persona and voice</p></blockquote></div><p>Finally, anyone who uses Sonos portable speakers like the <a href="https://www.tomsguide.com/reviews/sonos-move-2">Move 2</a> or <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> will be able to use them in a future update as surround sound speakers for the Beam Ultra. This feature, dubbed Sonos Fabric, uses high-frequency sound that enables Sonos products to “sense each other” and “adapt to the moment.” </p><p>We'll certainly test that ourselves when the feature becomes available at some point in the future.</p><h2 id="sony-eat-your-heart-out">Sony, eat your heart out</h2><p>Sonos isn't the only one going after the more modest soundbar market — this is Sony, Samsung and LG's bread and butter. </p><p>Each year, the trio of TV-makers deliver a half dozen of sub-$1,000 soundbars into the market, and every year they get gobbled up during sales events like Black Friday and, to a lesser extent, Super Bowl Sunday. </p><p>On paper, the Sonos Beam Ultra delivers a lot of value — potentially a whole lot more than what the competition offers — but $700 is still a pretty hefty chunk of change. </p><p>Is it worth the scratch? We'll know more after we hear it for ourselves at the New York event and bring it into our own homes for comparison testing sometime around its September 29 launch date.</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/noble-audio-iris-iems-review">I tested Noble's high-end IEMs and they boast an unbelievably wide soundstage — but are the Iris worth $699?</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/sony-bravia-theater-bar-7-review">I tested the Sony Bravia Theater Bar 7 + subwoofer — and recommending it is a no-brainer</a></li><li><a href="https://www.tomsguide.com/audio/speakers/before-you-buy-your-next-pair-of-speakers-make-sure-you-know-this-one-essential-difference">Before you buy your next pair of speakers, make sure you know this one essential difference</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sonos 27 is here to clean up the app mess (again) — this time with AI ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Sonos is giving its audio operating system a huge update that adds several AI tools and a speaker sensing tool that could change how you use your speakers: Dubbed Sonos 27 and announced at an event in NYC, this is the latest version of the company's control app and promises to be open and adaptable. </p><p>"We built our system around a shared love of music and the belief that nothing sets the mood like great sound," CEO Tom Conrad said in a press release. "Sonos 27 takes that further, bringing acoustic engineering, adaptable software,  intelligence and design together in a way only Sonos can."</p><p>The last time Sonos attempted an overhaul, the controversial redesign <a href="https://www.tomsguide.com/audio/speakers/sonos-ceo-exits-following-major-app-fail-but-theres-good-news">cost CEO Patrick Spence his job</a>. After public outcry, it took nearly a year for <a href="https://www.tomsguide.com/audio/speakers/it-works-new-sonos-update-might-have-just-fixed-this-huge-app-headache">Sonos to bring the app back</a> to an acceptable state. Here's hoping history isn't about to repeat itself with Sonos 27.</p><h2 id="what-39-s-new-with-sonos-27">What's new with Sonos 27? </h2><p>Sonos 27 comes with two AI features, Sonos 27voice and Sonos 27mcp, both of which utilize AI to change how controlling Sonos systems works.</p><p>Voice is a Sonos-built assistant for music and mood curation. The company is also introducing Sonos Custom Agents, which would allow users to create their own AI agents through the system, "each with its own model, persona and voice." Mcp lets you connect an outside AI chatbot to your Sonos system, including OpenAI's ChatGPT. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OqgPZe"></div>                            </div>                            <script src="https://kwizly.com/embed/OqgPZe.js" async></script><div><blockquote><p>Mcp lets you connect an outside AI chatbot to your Sonos system, including OpenAI's ChatGPT. </p></blockquote></div><p>Sonos claims you could ask ChatGPT to play music anywhere in your home without leaving your AI chat. </p><p>Sonos didn't provide many details in its press release on how Custom Agents or AI assistant integration will actually work or how you'll connect them to your system, but these details are likely to be discussed during the event in New York City.</p><h2 id="other-more-interesting-features">Other more interesting 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:4594px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Gingaw7NhxbjL9HBYDunjV" name="IMG_6876.JPG" alt="Sonos Play" src="https://cdn.mos.cms.futurecdn.net/Gingaw7NhxbjL9HBYDunjV.jpg" mos="" align="middle" fullscreen="" width="4594" height="2584" 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 more interesting features Sonos announced is called Sonos Fabric, a system capability that is supposed to enable Sonos devices to "sense each other" and then adapt to positioning. </p><p>According to Sonos, the positioning technology uses high-frequency sound to sense a speaker's location and then adapt its role in the system based on that. </p><p>Why is this necessary? It specifically enables portable surround sound features that let the <a href="https://www.tomsguide.com/reviews/sonos-move-2">Move 2</a> or <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> become rear surrounds when they're moved into position. (It's unclear whether this will be added to non-portable speakers.)</p><p>Another new feature launching in Sonos 27 will allow linking the Sonos Ace Ultra (also unveiled today) directly to your speaker system, letting you push or pull sound to and from speakers and your headphones. This is launching in early access to start, so you'll need to sign up for that.</p><p>Finally, Sonos is adding new features that are supposed to make the app "more intuitive to navigate." Sonos claims these new features were shaped and tested by the Sonos community; hopefully it doesn't break the app like the last update.</p><h2 id="so-when-will-it-be-available">So when will it be available? </h2><p>Sonos 27 will be available for all Sonos S2 products, though some features are only available on certain devices. (More details to come soon.)</p><p>The new app navigation features rolls out on September 8, and Sonos 27mcp launches in early access at the same time. </p><p>This will all roughly align with the launch of two new Sonos products — the Sonos Beam Ultra soundbar and Sonos Ace Ultra headphones — that launch on September 29. The Beam Ultra is an updated version of the <a href="https://www.tomsguide.com/reviews/sonos-beam-gen-2">Sonos Beam Gen 2 </a>mid-sized soundbar that will come in at  $699, while the Sonos Ace Ultra headphones are a sequel to the original <a href="https://www.tomsguide.com/audio/over-ear-headphones/sonos-ace">Sonos Ace </a>that will cost $449 at launch. </p><p>Both are available for pre-order now.</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/noble-audio-iris-iems-review">I tested Noble's high-end IEMs and they boast an unbelievably wide soundstage — but are the Iris worth $699?</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/sony-bravia-theater-bar-7-review">I tested the Sony Bravia Theater Bar 7 + subwoofer — and recommending it is a no-brainer</a></li><li><a href="https://www.tomsguide.com/audio/speakers/before-you-buy-your-next-pair-of-speakers-make-sure-you-know-this-one-essential-difference">Before you buy your next pair of speakers, make sure you know this one essential difference</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/speakers/sonos-27-is-here-to-clean-up-the-app-mess-again-this-time-with-ai</link>
                                                                            <description>
                            <![CDATA[ Sonos has a new version of its audio operating system that integrates AI. Here's how it should work. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">eDXyTteqwQmzJFJ5V26Kz8</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/DSrDWQZRHNMcoHdNE5HMZn-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 01 Sep 2026 14:40:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Speakers]]></category>
                                                    <category><![CDATA[Audio]]></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.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;br&gt;Send secure tips on Signal: scyo1.55.&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/DSrDWQZRHNMcoHdNE5HMZn-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sonos Arc Ultra under a TV]]></media:description>                                                            <media:text><![CDATA[Sonos Arc Ultra under a TV]]></media:text>
                                <media:title type="plain"><![CDATA[Sonos Arc Ultra under a TV]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/DSrDWQZRHNMcoHdNE5HMZn-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Sonos is giving its audio operating system a huge update that adds several AI tools and a speaker sensing tool that could change how you use your speakers: Dubbed Sonos 27 and announced at an event in NYC, this is the latest version of the company's control app and promises to be open and adaptable. </p><p>"We built our system around a shared love of music and the belief that nothing sets the mood like great sound," CEO Tom Conrad said in a press release. "Sonos 27 takes that further, bringing acoustic engineering, adaptable software,  intelligence and design together in a way only Sonos can."</p><p>The last time Sonos attempted an overhaul, the controversial redesign <a href="https://www.tomsguide.com/audio/speakers/sonos-ceo-exits-following-major-app-fail-but-theres-good-news">cost CEO Patrick Spence his job</a>. After public outcry, it took nearly a year for <a href="https://www.tomsguide.com/audio/speakers/it-works-new-sonos-update-might-have-just-fixed-this-huge-app-headache">Sonos to bring the app back</a> to an acceptable state. Here's hoping history isn't about to repeat itself with Sonos 27.</p><h2 id="what-39-s-new-with-sonos-27">What's new with Sonos 27? </h2><p>Sonos 27 comes with two AI features, Sonos 27voice and Sonos 27mcp, both of which utilize AI to change how controlling Sonos systems works.</p><p>Voice is a Sonos-built assistant for music and mood curation. The company is also introducing Sonos Custom Agents, which would allow users to create their own AI agents through the system, "each with its own model, persona and voice." Mcp lets you connect an outside AI chatbot to your Sonos system, including OpenAI's ChatGPT. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OqgPZe"></div>                            </div>                            <script src="https://kwizly.com/embed/OqgPZe.js" async></script><div><blockquote><p>Mcp lets you connect an outside AI chatbot to your Sonos system, including OpenAI's ChatGPT. </p></blockquote></div><p>Sonos claims you could ask ChatGPT to play music anywhere in your home without leaving your AI chat. </p><p>Sonos didn't provide many details in its press release on how Custom Agents or AI assistant integration will actually work or how you'll connect them to your system, but these details are likely to be discussed during the event in New York City.</p><h2 id="other-more-interesting-features">Other more interesting 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:4594px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Gingaw7NhxbjL9HBYDunjV" name="IMG_6876.JPG" alt="Sonos Play" src="https://cdn.mos.cms.futurecdn.net/Gingaw7NhxbjL9HBYDunjV.jpg" mos="" align="middle" fullscreen="" width="4594" height="2584" 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 more interesting features Sonos announced is called Sonos Fabric, a system capability that is supposed to enable Sonos devices to "sense each other" and then adapt to positioning. </p><p>According to Sonos, the positioning technology uses high-frequency sound to sense a speaker's location and then adapt its role in the system based on that. </p><p>Why is this necessary? It specifically enables portable surround sound features that let the <a href="https://www.tomsguide.com/reviews/sonos-move-2">Move 2</a> or <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> become rear surrounds when they're moved into position. (It's unclear whether this will be added to non-portable speakers.)</p><p>Another new feature launching in Sonos 27 will allow linking the Sonos Ace Ultra (also unveiled today) directly to your speaker system, letting you push or pull sound to and from speakers and your headphones. This is launching in early access to start, so you'll need to sign up for that.</p><p>Finally, Sonos is adding new features that are supposed to make the app "more intuitive to navigate." Sonos claims these new features were shaped and tested by the Sonos community; hopefully it doesn't break the app like the last update.</p><h2 id="so-when-will-it-be-available">So when will it be available? </h2><p>Sonos 27 will be available for all Sonos S2 products, though some features are only available on certain devices. (More details to come soon.)</p><p>The new app navigation features rolls out on September 8, and Sonos 27mcp launches in early access at the same time. </p><p>This will all roughly align with the launch of two new Sonos products — the Sonos Beam Ultra soundbar and Sonos Ace Ultra headphones — that launch on September 29. The Beam Ultra is an updated version of the <a href="https://www.tomsguide.com/reviews/sonos-beam-gen-2">Sonos Beam Gen 2 </a>mid-sized soundbar that will come in at  $699, while the Sonos Ace Ultra headphones are a sequel to the original <a href="https://www.tomsguide.com/audio/over-ear-headphones/sonos-ace">Sonos Ace </a>that will cost $449 at launch. </p><p>Both are available for pre-order now.</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/noble-audio-iris-iems-review">I tested Noble's high-end IEMs and they boast an unbelievably wide soundstage — but are the Iris worth $699?</a></li><li><a href="https://www.tomsguide.com/audio/soundbars/sony-bravia-theater-bar-7-review">I tested the Sony Bravia Theater Bar 7 + subwoofer — and recommending it is a no-brainer</a></li><li><a href="https://www.tomsguide.com/audio/speakers/before-you-buy-your-next-pair-of-speakers-make-sure-you-know-this-one-essential-difference">Before you buy your next pair of speakers, make sure you know this one essential difference</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I found 9 Sonos deals in Best Buy's Labor Day sale — save up to 21% right now ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Labor Day weekend is almost upon us and the sales have already started. Best Buy is running a wide-range of offers at the moment including one focused on Sonos speakers and soundbars. If you've been planning to upgrade your home entertainment setup, now is a great time to do so.</p><p>Amid the items on offer are <a href="https://www.tomsguide.com/reviews/sonos-era-300">Sonos Era 300 Smart Speaker</a>, which we rated 4.5 stars out of 5, as well as the <a href="https://www.tomsguide.com/reviews/sonos-sub-mini">Sonos Sub Mini Wireless Subwoofer</a>, which we gave the full 5 stars. These are great devices that will bring high quality audio to your TV and movie-viewing experience, as well as making music really pop. Check out all nine of the available Sonos deals below.</p>        <div class="featured_product_block featured_block_hero" data-id="c0fff3fc-a5f6-11f1-8385-63c96ae4aca8">            <a href="https://www.bestbuy.com/product/sonos-era-300-smart-speaker-with-spatial-audio-each-black/J39H3736FZ/sku/6535189" data-model-name="Era 300 Smart 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/VzpyM36rK6kUAbhXX7PMQ.jpg" alt="Sonos, Sonos - Era 300 Smart Speaker With Spatial Audio (each) - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Era 300 Smart Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff55a-a5f6-11f1-9304-bdbd86e47408">            <a href="https://www.bestbuy.com/product/sonos-era-300-smart-speaker-with-spatial-audio-each-white/J39H3736F6/sku/6535188" data-model-name="Era 300 Smart 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/m4Fq6SgcCkR7A3S89MuRFK.jpg" alt="BRAND, Sonos - Era 300 Smart Speaker with Spatial Audio (Each) - White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Era 300 Smart Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff730-a5f6-11f1-88a0-ffe4a7085132">            <a href="https://www.bestbuy.com/product/sonos-sub-mini-wireless-subwoofer-white/J39H373SPV/sku/6520133" data-model-name="Sub Mini Wireless Subwoofer" 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/Ec4bU9AGPSXVpo2BsK4WBN.jpg" alt="Sonos, Sub Mini Wireless Subwoofer"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Sub Mini Wireless Subwoofer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff5d2-a5f6-11f1-97bf-83785a68e0b1">            <a href="https://www.bestbuy.com/product/sonos-sub-mini-wireless-subwoofer-black/J39H373SS8/sku/6520132" data-model-name="Sub Mini Wireless Subwoofer" 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/4iuCjKhcGC2sEjCvg2mDdP.jpg" alt="Sonos - Sub Mini Wireless Subwoofer - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Sub Mini Wireless Subwoofer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff6c2-a5f6-11f1-aa6b-7dcc8bf87666">            <a href="https://www.bestbuy.com/product/sonos-beam-gen-2-5-0-channel-smart-soundbar-with-dolby-atmos-white/J39H373LTP/sku/6476040" data-model-name="Beam (gen 2) 5.0 Channel Smart Soundbar" 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/XkZPCg9sw8dzAG5orXb8Za.jpg" alt="Sonos, Sonos - Beam (gen 2) 5.0 Channel Smart Soundbar With Dolby Atmos - White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Beam (gen 2) 5.0 Channel Smart Soundbar</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff474-a5f6-11f1-ab7b-75b6e53fa6b8">            <a href="https://www.bestbuy.com/product/sonos-beam-gen-2-5-0-channel-smart-soundbar-with-dolby-atmos-black/J39H373LWC/sku/6476041" data-model-name="Beam (gen 2) 5.0 Channel Smart Soundbar" 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/CUam9LquQFCwn233ofKeu8.jpg" alt="Sonos, Sonos - Beam (gen 2) 5.0 Channel Smart Soundbar With Dolby Atmos - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Beam (gen 2) 5.0 Channel Smart Soundbar</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff4e2-a5f6-11f1-95bf-a18395a8aeb5">            <a href="https://www.bestbuy.com/product/sonos-sub-4-wireless-subwoofer-black/J39H37879G/sku/6597168" data-model-name="Sub 4 Wireless Subwoofer" 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/3goSFz7wqBbcLFLC3cjySC.jpg" alt="Sonos, Sonos - Sub 4 Wireless Subwoofer - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Sub 4 Wireless Subwoofer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff37a-a5f6-11f1-add0-bb8878425934">            <a href="https://www.bestbuy.com/product/sonos-arc-ultra-9-1-4-channel-smart-soundbar-with-dolby-atmos-black/J39H373F2S/sku/6597167" data-model-name="Arc Ultra 9.1.4 Channel Smart Soundbar" 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/X6ZBq26yRtkqRM3AaW23vh.jpg" alt="Sonos, Sonos - Arc Ultra 9.1.4 Channel Smart Soundbar With Dolby Atmos - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Arc Ultra 9.1.4 Channel Smart Soundbar</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff64a-a5f6-11f1-9b27-6d4f8bb38486">            <a href="https://www.bestbuy.com/product/sonos-arc-ultra-9-1-4-channel-smart-soundbar-with-dolby-atmos-white/J39H373F2X/sku/6597166" data-model-name="Arc Ultra 9.1.4 Channel Smart Soundbar" 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/je9XmwWx7dgUwwiFYXwgoW.jpg" alt="BRAND, Sonos - Arc Ultra 9.1.4 Channel Smart Soundbar with Dolby Atmos - White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Arc Ultra 9.1.4 Channel Smart Soundbar</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/smart-speakers/i-found-9-sonos-deals-in-best-buys-labor-day-sale-save-up-to-21-percent-right-now</link>
                                                                            <description>
                            <![CDATA[ Best Buy's Labor Day sale has slashed prices on a range of Sonos soundbars and speakers. Save up to 21% on these premium devices and build the home theater of your dreams. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">wqSNRDnCYTNPjDJw9qHvVT</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/JV27zGsVxb9E3qxjVLzqvE-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 01 Sep 2026 14:20:37 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Smart 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.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/JV27zGsVxb9E3qxjVLzqvE-1280-80.jpg">
                                                            <media:credit><![CDATA[Sonos / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of Sonos speakers in a room. ]]></media:description>                                                            <media:text><![CDATA[A selection of Sonos speakers in a room. ]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of Sonos speakers in a room. ]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/JV27zGsVxb9E3qxjVLzqvE-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Labor Day weekend is almost upon us and the sales have already started. Best Buy is running a wide-range of offers at the moment including one focused on Sonos speakers and soundbars. If you've been planning to upgrade your home entertainment setup, now is a great time to do so.</p><p>Amid the items on offer are <a href="https://www.tomsguide.com/reviews/sonos-era-300">Sonos Era 300 Smart Speaker</a>, which we rated 4.5 stars out of 5, as well as the <a href="https://www.tomsguide.com/reviews/sonos-sub-mini">Sonos Sub Mini Wireless Subwoofer</a>, which we gave the full 5 stars. These are great devices that will bring high quality audio to your TV and movie-viewing experience, as well as making music really pop. Check out all nine of the available Sonos deals below.</p>        <div class="featured_product_block featured_block_hero" data-id="c0fff3fc-a5f6-11f1-8385-63c96ae4aca8">            <a href="https://www.bestbuy.com/product/sonos-era-300-smart-speaker-with-spatial-audio-each-black/J39H3736FZ/sku/6535189" data-model-name="Era 300 Smart 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/VzpyM36rK6kUAbhXX7PMQ.jpg" alt="Sonos, Sonos - Era 300 Smart Speaker With Spatial Audio (each) - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Era 300 Smart Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff55a-a5f6-11f1-9304-bdbd86e47408">            <a href="https://www.bestbuy.com/product/sonos-era-300-smart-speaker-with-spatial-audio-each-white/J39H3736F6/sku/6535188" data-model-name="Era 300 Smart 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/m4Fq6SgcCkR7A3S89MuRFK.jpg" alt="BRAND, Sonos - Era 300 Smart Speaker with Spatial Audio (Each) - White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Era 300 Smart Speaker</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff730-a5f6-11f1-88a0-ffe4a7085132">            <a href="https://www.bestbuy.com/product/sonos-sub-mini-wireless-subwoofer-white/J39H373SPV/sku/6520133" data-model-name="Sub Mini Wireless Subwoofer" 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/Ec4bU9AGPSXVpo2BsK4WBN.jpg" alt="Sonos, Sub Mini Wireless Subwoofer"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Sub Mini Wireless Subwoofer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff5d2-a5f6-11f1-97bf-83785a68e0b1">            <a href="https://www.bestbuy.com/product/sonos-sub-mini-wireless-subwoofer-black/J39H373SS8/sku/6520132" data-model-name="Sub Mini Wireless Subwoofer" 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/4iuCjKhcGC2sEjCvg2mDdP.jpg" alt="Sonos - Sub Mini Wireless Subwoofer - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Sub Mini Wireless Subwoofer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff6c2-a5f6-11f1-aa6b-7dcc8bf87666">            <a href="https://www.bestbuy.com/product/sonos-beam-gen-2-5-0-channel-smart-soundbar-with-dolby-atmos-white/J39H373LTP/sku/6476040" data-model-name="Beam (gen 2) 5.0 Channel Smart Soundbar" 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/XkZPCg9sw8dzAG5orXb8Za.jpg" alt="Sonos, Sonos - Beam (gen 2) 5.0 Channel Smart Soundbar With Dolby Atmos - White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Beam (gen 2) 5.0 Channel Smart Soundbar</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff474-a5f6-11f1-ab7b-75b6e53fa6b8">            <a href="https://www.bestbuy.com/product/sonos-beam-gen-2-5-0-channel-smart-soundbar-with-dolby-atmos-black/J39H373LWC/sku/6476041" data-model-name="Beam (gen 2) 5.0 Channel Smart Soundbar" 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/CUam9LquQFCwn233ofKeu8.jpg" alt="Sonos, Sonos - Beam (gen 2) 5.0 Channel Smart Soundbar With Dolby Atmos - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Beam (gen 2) 5.0 Channel Smart Soundbar</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff4e2-a5f6-11f1-95bf-a18395a8aeb5">            <a href="https://www.bestbuy.com/product/sonos-sub-4-wireless-subwoofer-black/J39H37879G/sku/6597168" data-model-name="Sub 4 Wireless Subwoofer" 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/3goSFz7wqBbcLFLC3cjySC.jpg" alt="Sonos, Sonos - Sub 4 Wireless Subwoofer - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Sub 4 Wireless Subwoofer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff37a-a5f6-11f1-add0-bb8878425934">            <a href="https://www.bestbuy.com/product/sonos-arc-ultra-9-1-4-channel-smart-soundbar-with-dolby-atmos-black/J39H373F2S/sku/6597167" data-model-name="Arc Ultra 9.1.4 Channel Smart Soundbar" 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/X6ZBq26yRtkqRM3AaW23vh.jpg" alt="Sonos, Sonos - Arc Ultra 9.1.4 Channel Smart Soundbar With Dolby Atmos - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Arc Ultra 9.1.4 Channel Smart Soundbar</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c0fff64a-a5f6-11f1-9b27-6d4f8bb38486">            <a href="https://www.bestbuy.com/product/sonos-arc-ultra-9-1-4-channel-smart-soundbar-with-dolby-atmos-white/J39H373F2X/sku/6597166" data-model-name="Arc Ultra 9.1.4 Channel Smart Soundbar" 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/je9XmwWx7dgUwwiFYXwgoW.jpg" alt="BRAND, Sonos - Arc Ultra 9.1.4 Channel Smart Soundbar with Dolby Atmos - White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sonos</div>                                        <div class="featured__title">Arc Ultra 9.1.4 Channel Smart Soundbar</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested Noble's high-end IEMs and they boast an unbelievably wide soundstage — but are the Iris worth $699? ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Noble Audio is no stranger to making expensive, high-end audio gear that makes your ears smile. Having spent a week listening to my favorite tunes through the Noble Audio Iris, I have to say, they’re phenomenal. Intricately and beautifully designed, the Iris are a sight for sore eyes, and they’re built well, too. That’s not even the best part. The Iris boast an extremely wide soundstage with airy treble and accurate imaging.</p><p>The Iris are ridiculously comfortable IEMs, too, and you can wear them all day long with no problem. But let’s talk about the elephant in the room: the eye-watering $699 price tag. Are they worth the premium when the bass lacks a punch (but that’s down to their neutral-sounding tuning) and plug replacement is a little finicky? I’m <em>almost</em> convinced that they are.</p><p>To find out if these are the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best wired headphones</a> for you, read my full Noble Audio Iris review.</p><h2 class="article-body__section" id="section-noble-audio-iris-review-specs"><span>Noble Audio Iris review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://nobleaudio.com/products/iris" target="_blank" rel="nofollow">$699</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>Single 10.2mm custom dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>3.5mm or 4.4mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>Wired</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>0.5oz (each earpiece)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Blue</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-noble-audio-iris-review-price-availability"><span>Noble Audio Iris 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="9zsjmusGEwQWuHWNC8s5WJ" name="Noble_Audio_Iris_004.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/9zsjmusGEwQWuHWNC8s5WJ.jpg" 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>Noble Audio’s headphones, earbuds and in-ear monitors (IEMs) rarely come cheap, and the same goes for the Noble Audio Iris. These IEMs retail for <a href="https://nobleaudio.com/products/iris" target="_blank" rel="nofollow">$699</a> and can be bought directly from the American manufacturer. The Iris launched in July 2026 alongside the Vanguard, which are slightly cheaper at <a href="https://nobleaudio.com/products/vanguard" target="_blank" rel="nofollow">$325</a>.</p><p>Though they sound extremely expensive, the Iris are some of Noble Audio’s cheapest IEMs, with the next most expensive IEMs, the Lu Ban, retailing for <a href="https://nobleaudio.com/products/lu-ban">$1,399</a> — and the Kronos, at $4,500, are the brand’s priciest monitors. Given that context, the Iris are Noble Audio’s budget-ish IEMs.</p><p>The Iris find themselves competing with other high-end IEMs, like the <a href="https://www.tomsguide.com/reviews/sennheiser-ie-600-review">Sennheiser IE 600</a> ($699) and the Shanling Moria (<a href="https://en.shanling.com/product/497" target="_blank" rel="nofollow">$699</a>). Are the Iris worth the money? Oh hell yes! But if you can’t afford them, the <a href="https://www.tomsguide.com/audio/earbuds/sivga-lyrebird-iems-review">Sivga Lyrebird</a> ($149) and <a href="https://www.tomsguide.com/audio/earbuds/sennheiser-ie-200-wired-earbuds-review">Sennheiser IE 200</a> ($149) are phenomenal budget-friendly IEMs.</p><h2 class="article-body__section" id="section-noble-audio-iris-review-design"><span>Noble Audio Iris 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="CZsjniZtHuYoKoikTh3RTJ" name="Noble_Audio_Iris_010.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/CZsjniZtHuYoKoikTh3RTJ.jpg" 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 Noble Audio Iris are some of the most beautiful in-ear monitors I’ve laid my eyes on. When I first unboxed them, I couldn’t stop staring at them. They feature a blue CNC-machined Micarta (a durable material made by stacking layers under heat) shell. Noble Audio’s crown insignia is engraved on and adorns each ear piece. Given their name, you can tell that they’ve been inspired by an iris flower. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3ez4fEAza92gmzWm8pc7YJ" name="Noble_Audio_Iris_006.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/3ez4fEAza92gmzWm8pc7YJ.jpg" 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 addition to looking downright gorgeous, the Iris are built extremely well and they feel solid — and let’s be honest, you wouldn’t expect any less for the premium you’re paying. The anti-tangle cable is made of high-purity copper and it looks and feels great. Similar to other IEMs, the cable can be detached from the ear pieces and replaced with another if it gets damaged.</p><h2 id="weatherproofing-3">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qhsNAWqVNZbvvQvP5TphWJ" name="Noble_Audio_Iris_012.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/qhsNAWqVNZbvvQvP5TphWJ.jpg" 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>Similar to other IEMs I’ve tested, the Iris aren’t waterproof or weather-sealed. This isn’t uncommon for IEMs, though the <a href="https://www.tomsguide.com/audio/earbuds/beyerdynamic-dt-30-ie-iems-review">Beyerdynamic DT 30 IE</a> ($159) are IP54-rated splash-proof — but they don’t sound anywhere near as good as the Iris. Since Noble Audio doesn’t specify an IP rating, I wouldn’t recommend wearing the Iris in the rain or during sweaty workouts. But that’s not really what IEMs are designed for anyway.</p><h2 id="comfort-4">Comfort</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3425px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="GoeofiWt9tDt6vrYKJJd8E" name="Noble_Audio_IRIS-2" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/GoeofiWt9tDt6vrYKJJd8E.jpg" mos="" align="middle" fullscreen="" width="3425" height="1927" 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>When I first held the Iris in my palm, they felt substantial and a little heavy, and I was concerned that they wouldn’t feel very comfortable for extended wear. Boy, was I wrong. The Iris are supremely comfortable IEMs. Each ear piece weighs 0.5oz, so while they’re slightly heavier than the Sennheiser IE 600 (0.21oz each), they don’t feel unwieldy.</p><p>They fit snugly in my ears, and the braided cable wraps comfortably around each ear. The Iris didn’t fall out of my ears, even when I was bobbing my head. I wore them for a six-hour stretch without feeling any discomfort.</p><h2 id="controls-7">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="mNZfuDAspyVK55jvXJhDUJ" name="Noble_Audio_Iris_007.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/mNZfuDAspyVK55jvXJhDUJ.jpg" 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 Iris don’t feature an in-line remote for controlling playback, so you’ll need to use your smartphone, laptop or other audio source to play, pause, rewind and skip tracks. Again, not uncommon with IEMs.</p><h2 id="connectivity-amp-drive">Connectivity & drive</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iTpk2mhSzbis3x9XBnjYXJ" name="Noble_Audio_Iris_003.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/iTpk2mhSzbis3x9XBnjYXJ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As is the nature of wired IEMs, the Iris need to be driven, or powered, by a power source, and there are two ways of doing so. In the box, you’ll find a 3.5mm plug as well as a 4.4mm one, and you can easily swap between the two as and when needed. The 3.5mm plug is handy for using the Iris with your laptop, while the 4.4mm jack (which separates audio into left and right channels to keep interference to a minimum) can be used with a DAC/amp, like the FiiO K11 R2R (<a href="https://www.amazon.co.uk/dp/B0D9BB5RL1" target="_blank" rel="nofollow">$175</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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="B3se9a6jqo7Lrh4vJJtaTJ" name="Noble_Audio_Iris_015.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/B3se9a6jqo7Lrh4vJJtaTJ.jpg" 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 theory, the Iris’ termination plug should be super easy to remove. All you need to do is unscrew the bolt and then pull out the one that’s already attached, line up the four alignment pins correctly, and push it in. <em>Et voilà. </em>But I struggled to do so, and so did three of my coworkers. There’s a joke in there somewhere, along the lines of, “How many tech journalists does it take to unscrew a bolt?” It’s worth noting, though, that this could be down to my Iris being a pre-production sample, and this may not apply to everyone. Just try not to overtorque the fastener.</p><h2 class="article-body__section" id="section-noble-audio-iris-review-sound-quality"><span>Noble Audio Iris 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:7496px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="ZeP5gZJWfAFM4Rc3HZxQkE" name="Noble_Audio_IRIS" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/ZeP5gZJWfAFM4Rc3HZxQkE.jpg" mos="" align="middle" fullscreen="" width="7496" height="4217" 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 Sivga Lyrebird which use a quad-driver setup, or the Noble Audio Vanguard with their triple-driver configuration, the Noble Audio Iris utilizes a single 10.2mm custom dynamic driver. The single driver eliminates crossover distortion as it handles the entire frequency spectrum on its own. The Iris also features a three-layer diaphragm and a triple-chamber acoustic structure to reduce distortion and improve sound clarity.</p><p>All of these elements come together to deliver an astonishing, sublime listening experience that makes the Iris (almost) worth their asking price. I tested the Iris by listening to my favorite tunes on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles, using the 4.4mm jack plugged into the FiiO K11 R2R.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VGQRd2gNGkGfjza48bpxTJ" name="Noble_Audio_Iris_013.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/VGQRd2gNGkGfjza48bpxTJ.jpg" 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 listening to ‘<a href="https://open.qobuz.com/track/47683554" target="_blank" rel="nofollow">Time</a>’ by Pink Floyd, I immediately appreciated the Iris’ wide soundstage which gave the track’s layered production plenty of room to breathe. Imaging feels extremely accurate as the individual percussion hits, guitar lines and atmospheric details occupied well-defined positions. The opening clocks felt especially immersive, spreading naturally across the soundstage rather than forming a flat wall of sound. Instrument separation feels excellent, too, and I felt like I was finding new details in my favorite song of all time.</p><p>I then listened to ‘<a href="https://open.qobuz.com/track/20260078" target="_blank" rel="nofollow">Misty Mountain Hop</a>’ by Led Zeppelin to see how the Iris handled the upper frequencies and crunchy guitar slides. The IEMs gave me newfound appreciation for the track as they reproduced the energetic character accurately, and there was plenty of sparkle in the percussion. The bite of the guitar came through with precision without making the song feel overly sharp or unpleasant.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YckBTPDwEidq3H4dbDUzXJ" name="Noble_Audio_Iris_002.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/YckBTPDwEidq3H4dbDUzXJ.jpg" 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>Billie Eilish’s ‘<a href="https://open.qobuz.com/track/266725034" target="_blank" rel="nofollow">BITTERSUITE</a>’ proved to be an excellent test for the Iris’ mid-range, and one the IEMs passed with flying colors. Vocal presentation feels beautiful, as Billie’s haunting vocals sat front and center, sounded natural, and the Iris ensured that the instrumental arrangement didn’t get lost either. Everything felt well-balanced. I loved how textured and smooth the vocals sounded, with subtle inflections and breathiness coming through without sounding exaggerated.</p><p>The only area where the Iris fall short of perfection is bass, as I found out when listening to ‘<a href="https://open.qobuz.com/track/420633037" target="_blank" rel="nofollow">Baby Driver</a>’ by 070 Shake. The Iris made the track’s pounding bassline sound tight and controlled, but it lacked the <em>oomph </em>and pronounced rumble I thoroughly enjoyed through the Sivga Lyrebird. This was especially apparent in the distorted, electronic bassline leading up to the second verse. It’s supposed to drive the alternative R&B track but instead, the sheer force of it felt subdued. This isn't a flaw, per se, as much of Noble's gear is tuned to sound neutral, but just a pre-purchase consideration if you like meaty bass.</p><p><em>Listen along to my review with the Noble Audio Iris testing playlist:</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/69263257?zone=GB-en"></iframe><h2 class="article-body__section" id="section-noble-audio-iris-review-verdict"><span>Noble Audio Iris 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="RXzQe5dJj7hu7yRb3KkxPJ" name="Noble_Audio_Iris_009.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/RXzQe5dJj7hu7yRb3KkxPJ.jpg" 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 Noble Audio Iris are seriously impressive in-ear monitors, and after a week of testing, I can (almost) see why they cost as much as they do. They look drop-dead gorgeous, feel incredibly well made, and, most importantly, sound brilliant. The soundstage is extremely wide. The treble is oh-so-crispy. The mids are refined. The imaging is accurate. What more could you want?</p><p>Punchy bass, I suppose. The Iris make bass sound tight and controlled but they lack <em>oomph </em>— although this isn’t a dealbreaker as the Iris are tuned to sound neutral. It’s also great that you can swap between 3.5mm and 4.4mm plugs, but try not to overtorque the bolt.</p><p>And there’s the small matter of the price. $699 is a serious investment, even if the Iris are comparatively affordable by Noble’s standards. If all you care about is spacious, balanced and highly detailed sound, the Iris warrant a recommendation. At the end of the day, they’re beautiful, comfortable, and a joy to listen to.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/noble-audio-iris-iems-review</link>
                                                                            <description>
                            <![CDATA[ The Noble Audio Iris are extremely comfortable IEMs and they boast a wide soundstage, crisp treble and refined mids. They're built well and look stunning, too. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">j5ezhmyVSDPVRzoS6J2pnd</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/AwCJuoabfnxWHi9ywJhMmD-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 31 Aug 2026 09:00: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.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/AwCJuoabfnxWHi9ywJhMmD-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Noble Audio Iris in-ear monitors ]]></media:description>                                                            <media:text><![CDATA[Noble Audio Iris in-ear monitors ]]></media:text>
                                <media:title type="plain"><![CDATA[Noble Audio Iris in-ear monitors ]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/AwCJuoabfnxWHi9ywJhMmD-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Noble Audio is no stranger to making expensive, high-end audio gear that makes your ears smile. Having spent a week listening to my favorite tunes through the Noble Audio Iris, I have to say, they’re phenomenal. Intricately and beautifully designed, the Iris are a sight for sore eyes, and they’re built well, too. That’s not even the best part. The Iris boast an extremely wide soundstage with airy treble and accurate imaging.</p><p>The Iris are ridiculously comfortable IEMs, too, and you can wear them all day long with no problem. But let’s talk about the elephant in the room: the eye-watering $699 price tag. Are they worth the premium when the bass lacks a punch (but that’s down to their neutral-sounding tuning) and plug replacement is a little finicky? I’m <em>almost</em> convinced that they are.</p><p>To find out if these are the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best wired headphones</a> for you, read my full Noble Audio Iris review.</p><h2 class="article-body__section" id="section-noble-audio-iris-review-specs"><span>Noble Audio Iris review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://nobleaudio.com/products/iris" target="_blank" rel="nofollow">$699</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>Single 10.2mm custom dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>3.5mm or 4.4mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>Wired</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>0.5oz (each earpiece)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Blue</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-noble-audio-iris-review-price-availability"><span>Noble Audio Iris 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="9zsjmusGEwQWuHWNC8s5WJ" name="Noble_Audio_Iris_004.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/9zsjmusGEwQWuHWNC8s5WJ.jpg" 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>Noble Audio’s headphones, earbuds and in-ear monitors (IEMs) rarely come cheap, and the same goes for the Noble Audio Iris. These IEMs retail for <a href="https://nobleaudio.com/products/iris" target="_blank" rel="nofollow">$699</a> and can be bought directly from the American manufacturer. The Iris launched in July 2026 alongside the Vanguard, which are slightly cheaper at <a href="https://nobleaudio.com/products/vanguard" target="_blank" rel="nofollow">$325</a>.</p><p>Though they sound extremely expensive, the Iris are some of Noble Audio’s cheapest IEMs, with the next most expensive IEMs, the Lu Ban, retailing for <a href="https://nobleaudio.com/products/lu-ban">$1,399</a> — and the Kronos, at $4,500, are the brand’s priciest monitors. Given that context, the Iris are Noble Audio’s budget-ish IEMs.</p><p>The Iris find themselves competing with other high-end IEMs, like the <a href="https://www.tomsguide.com/reviews/sennheiser-ie-600-review">Sennheiser IE 600</a> ($699) and the Shanling Moria (<a href="https://en.shanling.com/product/497" target="_blank" rel="nofollow">$699</a>). Are the Iris worth the money? Oh hell yes! But if you can’t afford them, the <a href="https://www.tomsguide.com/audio/earbuds/sivga-lyrebird-iems-review">Sivga Lyrebird</a> ($149) and <a href="https://www.tomsguide.com/audio/earbuds/sennheiser-ie-200-wired-earbuds-review">Sennheiser IE 200</a> ($149) are phenomenal budget-friendly IEMs.</p><h2 class="article-body__section" id="section-noble-audio-iris-review-design"><span>Noble Audio Iris 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="CZsjniZtHuYoKoikTh3RTJ" name="Noble_Audio_Iris_010.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/CZsjniZtHuYoKoikTh3RTJ.jpg" 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 Noble Audio Iris are some of the most beautiful in-ear monitors I’ve laid my eyes on. When I first unboxed them, I couldn’t stop staring at them. They feature a blue CNC-machined Micarta (a durable material made by stacking layers under heat) shell. Noble Audio’s crown insignia is engraved on and adorns each ear piece. Given their name, you can tell that they’ve been inspired by an iris flower. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3ez4fEAza92gmzWm8pc7YJ" name="Noble_Audio_Iris_006.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/3ez4fEAza92gmzWm8pc7YJ.jpg" 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 addition to looking downright gorgeous, the Iris are built extremely well and they feel solid — and let’s be honest, you wouldn’t expect any less for the premium you’re paying. The anti-tangle cable is made of high-purity copper and it looks and feels great. Similar to other IEMs, the cable can be detached from the ear pieces and replaced with another if it gets damaged.</p><h2 id="weatherproofing-3">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qhsNAWqVNZbvvQvP5TphWJ" name="Noble_Audio_Iris_012.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/qhsNAWqVNZbvvQvP5TphWJ.jpg" 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>Similar to other IEMs I’ve tested, the Iris aren’t waterproof or weather-sealed. This isn’t uncommon for IEMs, though the <a href="https://www.tomsguide.com/audio/earbuds/beyerdynamic-dt-30-ie-iems-review">Beyerdynamic DT 30 IE</a> ($159) are IP54-rated splash-proof — but they don’t sound anywhere near as good as the Iris. Since Noble Audio doesn’t specify an IP rating, I wouldn’t recommend wearing the Iris in the rain or during sweaty workouts. But that’s not really what IEMs are designed for anyway.</p><h2 id="comfort-4">Comfort</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3425px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="GoeofiWt9tDt6vrYKJJd8E" name="Noble_Audio_IRIS-2" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/GoeofiWt9tDt6vrYKJJd8E.jpg" mos="" align="middle" fullscreen="" width="3425" height="1927" 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>When I first held the Iris in my palm, they felt substantial and a little heavy, and I was concerned that they wouldn’t feel very comfortable for extended wear. Boy, was I wrong. The Iris are supremely comfortable IEMs. Each ear piece weighs 0.5oz, so while they’re slightly heavier than the Sennheiser IE 600 (0.21oz each), they don’t feel unwieldy.</p><p>They fit snugly in my ears, and the braided cable wraps comfortably around each ear. The Iris didn’t fall out of my ears, even when I was bobbing my head. I wore them for a six-hour stretch without feeling any discomfort.</p><h2 id="controls-7">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="mNZfuDAspyVK55jvXJhDUJ" name="Noble_Audio_Iris_007.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/mNZfuDAspyVK55jvXJhDUJ.jpg" 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 Iris don’t feature an in-line remote for controlling playback, so you’ll need to use your smartphone, laptop or other audio source to play, pause, rewind and skip tracks. Again, not uncommon with IEMs.</p><h2 id="connectivity-amp-drive">Connectivity & drive</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iTpk2mhSzbis3x9XBnjYXJ" name="Noble_Audio_Iris_003.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/iTpk2mhSzbis3x9XBnjYXJ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As is the nature of wired IEMs, the Iris need to be driven, or powered, by a power source, and there are two ways of doing so. In the box, you’ll find a 3.5mm plug as well as a 4.4mm one, and you can easily swap between the two as and when needed. The 3.5mm plug is handy for using the Iris with your laptop, while the 4.4mm jack (which separates audio into left and right channels to keep interference to a minimum) can be used with a DAC/amp, like the FiiO K11 R2R (<a href="https://www.amazon.co.uk/dp/B0D9BB5RL1" target="_blank" rel="nofollow">$175</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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="B3se9a6jqo7Lrh4vJJtaTJ" name="Noble_Audio_Iris_015.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/B3se9a6jqo7Lrh4vJJtaTJ.jpg" 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 theory, the Iris’ termination plug should be super easy to remove. All you need to do is unscrew the bolt and then pull out the one that’s already attached, line up the four alignment pins correctly, and push it in. <em>Et voilà. </em>But I struggled to do so, and so did three of my coworkers. There’s a joke in there somewhere, along the lines of, “How many tech journalists does it take to unscrew a bolt?” It’s worth noting, though, that this could be down to my Iris being a pre-production sample, and this may not apply to everyone. Just try not to overtorque the fastener.</p><h2 class="article-body__section" id="section-noble-audio-iris-review-sound-quality"><span>Noble Audio Iris 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:7496px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="ZeP5gZJWfAFM4Rc3HZxQkE" name="Noble_Audio_IRIS" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/ZeP5gZJWfAFM4Rc3HZxQkE.jpg" mos="" align="middle" fullscreen="" width="7496" height="4217" 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 Sivga Lyrebird which use a quad-driver setup, or the Noble Audio Vanguard with their triple-driver configuration, the Noble Audio Iris utilizes a single 10.2mm custom dynamic driver. The single driver eliminates crossover distortion as it handles the entire frequency spectrum on its own. The Iris also features a three-layer diaphragm and a triple-chamber acoustic structure to reduce distortion and improve sound clarity.</p><p>All of these elements come together to deliver an astonishing, sublime listening experience that makes the Iris (almost) worth their asking price. I tested the Iris by listening to my favorite tunes on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles, using the 4.4mm jack plugged into the FiiO K11 R2R.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VGQRd2gNGkGfjza48bpxTJ" name="Noble_Audio_Iris_013.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/VGQRd2gNGkGfjza48bpxTJ.jpg" 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 listening to ‘<a href="https://open.qobuz.com/track/47683554" target="_blank" rel="nofollow">Time</a>’ by Pink Floyd, I immediately appreciated the Iris’ wide soundstage which gave the track’s layered production plenty of room to breathe. Imaging feels extremely accurate as the individual percussion hits, guitar lines and atmospheric details occupied well-defined positions. The opening clocks felt especially immersive, spreading naturally across the soundstage rather than forming a flat wall of sound. Instrument separation feels excellent, too, and I felt like I was finding new details in my favorite song of all time.</p><p>I then listened to ‘<a href="https://open.qobuz.com/track/20260078" target="_blank" rel="nofollow">Misty Mountain Hop</a>’ by Led Zeppelin to see how the Iris handled the upper frequencies and crunchy guitar slides. The IEMs gave me newfound appreciation for the track as they reproduced the energetic character accurately, and there was plenty of sparkle in the percussion. The bite of the guitar came through with precision without making the song feel overly sharp or unpleasant.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YckBTPDwEidq3H4dbDUzXJ" name="Noble_Audio_Iris_002.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/YckBTPDwEidq3H4dbDUzXJ.jpg" 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>Billie Eilish’s ‘<a href="https://open.qobuz.com/track/266725034" target="_blank" rel="nofollow">BITTERSUITE</a>’ proved to be an excellent test for the Iris’ mid-range, and one the IEMs passed with flying colors. Vocal presentation feels beautiful, as Billie’s haunting vocals sat front and center, sounded natural, and the Iris ensured that the instrumental arrangement didn’t get lost either. Everything felt well-balanced. I loved how textured and smooth the vocals sounded, with subtle inflections and breathiness coming through without sounding exaggerated.</p><p>The only area where the Iris fall short of perfection is bass, as I found out when listening to ‘<a href="https://open.qobuz.com/track/420633037" target="_blank" rel="nofollow">Baby Driver</a>’ by 070 Shake. The Iris made the track’s pounding bassline sound tight and controlled, but it lacked the <em>oomph </em>and pronounced rumble I thoroughly enjoyed through the Sivga Lyrebird. This was especially apparent in the distorted, electronic bassline leading up to the second verse. It’s supposed to drive the alternative R&B track but instead, the sheer force of it felt subdued. This isn't a flaw, per se, as much of Noble's gear is tuned to sound neutral, but just a pre-purchase consideration if you like meaty bass.</p><p><em>Listen along to my review with the Noble Audio Iris testing playlist:</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/69263257?zone=GB-en"></iframe><h2 class="article-body__section" id="section-noble-audio-iris-review-verdict"><span>Noble Audio Iris 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="RXzQe5dJj7hu7yRb3KkxPJ" name="Noble_Audio_Iris_009.JPG" alt="Noble Audio Iris in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/RXzQe5dJj7hu7yRb3KkxPJ.jpg" 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 Noble Audio Iris are seriously impressive in-ear monitors, and after a week of testing, I can (almost) see why they cost as much as they do. They look drop-dead gorgeous, feel incredibly well made, and, most importantly, sound brilliant. The soundstage is extremely wide. The treble is oh-so-crispy. The mids are refined. The imaging is accurate. What more could you want?</p><p>Punchy bass, I suppose. The Iris make bass sound tight and controlled but they lack <em>oomph </em>— although this isn’t a dealbreaker as the Iris are tuned to sound neutral. It’s also great that you can swap between 3.5mm and 4.4mm plugs, but try not to overtorque the bolt.</p><p>And there’s the small matter of the price. $699 is a serious investment, even if the Iris are comparatively affordable by Noble’s standards. If all you care about is spacious, balanced and highly detailed sound, the Iris warrant a recommendation. At the end of the day, they’re beautiful, comfortable, and a joy to listen to.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve been testing the Grado SR325: They’re not the most comfortable, but I could still listen for hours ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As someone who’s always listening to music and looking for the <a href="https://www.tomsguide.com/best-picks/best-over-ear-headphones">best headphones</a> to replicate the sound of my favorite artists, I was excited to test out the Grado SR325. I’m a big fan of detailed sound, and the Grados deliver it in abundance. </p><p>They have amazing vocal reproduction while also delivering detailed mids performance, so you can take in your favorite guitar riffs and drum fills. I found them best for mellow music, but they perform well across the board. </p><p>But while the design is sleek and retro, I had some issues. The on-ear style of the headphones isn’t for me. I found them uncomfortable to wear for long periods of time, and the cheap foam padding made this even worse. To find out more about the headphones, you can keep reading this full Grado SR325 review.</p><h2 class="article-body__section" id="section-grado-sr325-review-specs"><span>Grado SR325 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/GRADO-Classic-SR325-Open-Back-Headphones/dp/B0GTN8J2VW" target="_blank" rel="nofollow">$350</a> / <a href="https://www.grado.co.uk/classic-series/sr325-classic.html" target="_blank" rel="nofollow">£349</a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Open back, on-ear</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>44mm dynamic </p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>18-24 kHz</p></td></tr><tr><td class="firstcol " ><p><strong>Impdance</strong></p></td><td  ><p>38 ohms</p></td></tr><tr><td class="firstcol " ><p><strong>SPL 1mW</strong></p></td><td  ><p>98</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Wired, 3.5mm jack with 6.3mm adapter</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>14 ounces</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-grado-sr325-review-price-availability"><span>Grado SR325 review: Price & availability</span></h2><p>The Grado SR325 headphones are available in the U.S. on <a href="https://www.grado.co.uk/classic-series/sr325-classic.html" target="_blank" rel="nofollow">Amazon for $350</a> and in the U.K. on the <a href="https://www.grado.co.uk/classic-series/sr325-classic.html" target="_blank" rel="nofollow">Grado website for £349.</a> When it comes to open-back headphones, you’re likely to be forking out a fair bit of cash for great sound, so that’s to be expected.</p><p>For around the same price, you can pick up the <a href="https://www.tomsguide.com/audio/over-ear-headphones/fiio-ft13-review">FiiO FT13</a> ($329). These cans provide a deep, rich sound with impeccable instrument separation. Similarly, the <a href="https://www.tomsguide.com/audio/over-ear-headphones/meze-99-classics-2nd-gen-review">Meze 99 Classics Gen 2</a>, which cost $349, can provide an even cleaner sound. The Mezes are over-ear headphones with a light headband, making them comfortable to wear for long periods, unlike the Grados. </p><p>The <a href="https://www.tomsguide.com/audio/i-just-tested-these-wired-headphones-and-they-sound-absolutely-stunning-without-breaking-the-bank">Meze Audio 105 AER</a> are another solid choice, offering warm sound, some of the best clarity you’ll find, and much better comfort. That’s all for $299, which makes them a true no-brainer when compared with the questionable sound and $350 price tag of the Grado SR325.</p><h2 class="article-body__section" id="section-grado-sr325-review-design"><span>Grado SR325 review: Design </span></h2><p>The Grado SR325 are on-ear headphones with an open-back design. They have built-in wires, so there's no need to plug in each time you listen. The headphones look sleek with black aluminum details and a faux leather headband. I think they look great and more than a little retro, but the headband feels flimsy as it’s not very thick. The headphones don’t fold and don’t come with a case either, so you’d need to invest in a headphone stand to ensure they stay in tip-top condition.</p><h3 id="comfort-5">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gofZbkR2SAi6UDJJEuDEbP" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/gofZbkR2SAi6UDJJEuDEbP.jpg" 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>While the Grado SR325 are comfortable to wear for short listening sessions, I would much rather use a pair of over-ear headphones like the <a href="https://www.tomsguide.com/audio/over-ear-headphones/fiio-ft13-review">FiiO FT13</a> ($329) for comfort over extended periods of time. The on-ear style Grado headphones rest directly on my ears, making the cartilage ache, which is annoying if, like me, you want to listen to music for hours on end. Also, the cheap, thin foam used to cover the earcups is uncomfortable and a little bit scratchy. It reminds me of the foam covering on the <a href="https://www.tomsguide.com/audio/headphones/gadhouse-wesley-headphones-review">Gadhouse Wesley</a> headphones, which are significantly cheaper at $69. For $350, I expect more care to be taken with the comfort of the Grado cans. </p><p>The headphones weigh 14 ounces and didn’t feel too heavy, but the top of my head became sore where the headband was resting after about 45 minutes because there’s not much padding.</p><h3 id="connectivity-7">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="puyKNQ9wmSNEEo7k83hq8P" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/puyKNQ9wmSNEEo7k83hq8P.jpg" 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 Grado SR325 headphones have built-in wires connected to both earcups that merge into one aux wire. You have the choice between a 3.5mm jack or a 6.3mm adapter when connecting to your device. </p><p>While listening, I used the <a href="https://www.amazon.com/FiiO-K11-Headphone-Amplifier-Balanced/dp/B0D9BCRVXN" target="_blank" rel="nofollow">FiiO K11 R2R</a> DAC to power the headphones, and my colleagues also tested the cans with the iFi hip-dac 3 and FiiO K15. While the SR325 aren’t very hard to drive and can be powered by your laptop or phone, using a good DAC ensures they’ll sound their best, and I could hear a stark difference between listening with and without. </p><h2 class="article-body__section" id="section-grado-sr325-review-sound-quality"><span>Grado SR325 review: Sound quality</span></h2><p>The Grado SR325 headphones have a very interesting sound profile. It’s a sound I completely enjoy, and they accentuate different areas from other audiophile headphones I’ve used, but this original sound is fuzzy, adopting an analog tone, like a cup of tea on an autumn afternoon. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="DfRpqBJ28WrtGQQK3urhwN" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/DfRpqBJ28WrtGQQK3urhwN.jpg" 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 listened using <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, one of the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming services</a>, and used the FiiO K11 R2R desktop DAC to amplify the sound. The headphones offer a well-rounded sound across most genres. They thrive with simpler, acoustic music, but they also enhance layers in more complex tracks.</p><p>Firstly, I listened to <em>Music On The Radio</em> by Empire Of The Sun to test the bass performance. While it didn’t deliver the same depth of bass I can expect from my everyday drivers, which are the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">Sony WH-1000XM6</a>, I was able to appreciate all of the layers of the track without the bass becoming crackly or overwhelming. This means the headphones strike a good balance and allow me to hear more of the vocal performance. Bass certainly takes a backseat, but it’s still there, gently warming the track from the bottom up. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZFdk97EHxZsajMisVqps2P" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/ZFdk97EHxZsajMisVqps2P.jpg" 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 out the mid-range performance, I listened to <em>Birds</em> by Turnstile, as it has a prominent guitar riff complemented by heavy drums. The hi-hats stood out between the verse and the chorus, and the guitar riff was incredibly powerful when it kicked in. The soundstage wasn’t as wide as when I listen using my <a href="https://www.tomsguide.com/audio/headphones/beyerdynamic-dt-270-pro-review">Beyerdynamic DT 270 Pro</a>, but the level of detail made up for that. </p><p>Finally, to test the treble performance of the Grados, I listened to <em>The Last Man On Earth </em>by Wolf Alice. This song focuses on the vocal performance before exploding with the addition of harmonies, drums, and bass. The vocals were bright throughout and so detailed that I could hear every breath. When the drums kicked in, I experienced some very slight fuzziness, but that’s why I think these headphones do a better job with simple tracks. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Nyoj7uJq4jvKA4c59SHjaP" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/Nyoj7uJq4jvKA4c59SHjaP.jpg" 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>Overall, I think the Grado SR325 headphones are a great option for detailed listening, especially for people who like detailed tracks.</p><h2 class="article-body__section" id="section-grado-sr325-review-verdict"><span>Grado SR325 review: Verdict</span></h2><p>The Grado SR325 headphones have a sleek-looking, retro design that is sure to look great next to any audio setup. My favorite thing about these cans is the amazing vocal replication. The mid-range is also exceptionally detailed, with guitar riffs taking the forefront where needed. The warm sound makes these cans very pleasant to listen to. </p><p>And while the sleek, retro design looks good, I can’t help but notice that the foam padding on the earcups feels very cheap. This doesn’t help that the on-ear style is uncomfortable for long listening periods. </p><p>Overall, these headphones are a great choice, but for $350, you may be expecting a little more comfort with your sound. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/over-ear-headphones/grado-sr325-review</link>
                                                                            <description>
                            <![CDATA[ The Grado SR325 open-back headphones provide great vocal clarity and balance, but they could be more comfortable. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">xWrWUvNgYBfU49MDR86ggb</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/d9PJ2s9Mdx2vuYN7sPVhLP-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 31 Aug 2026 08:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Over-Ear Headphones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/d9PJ2s9Mdx2vuYN7sPVhLP-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Grado SR325 photographed in front of a blue background]]></media:description>                                                            <media:text><![CDATA[Grado SR325 photographed in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[Grado SR325 photographed in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/d9PJ2s9Mdx2vuYN7sPVhLP-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As someone who’s always listening to music and looking for the <a href="https://www.tomsguide.com/best-picks/best-over-ear-headphones">best headphones</a> to replicate the sound of my favorite artists, I was excited to test out the Grado SR325. I’m a big fan of detailed sound, and the Grados deliver it in abundance. </p><p>They have amazing vocal reproduction while also delivering detailed mids performance, so you can take in your favorite guitar riffs and drum fills. I found them best for mellow music, but they perform well across the board. </p><p>But while the design is sleek and retro, I had some issues. The on-ear style of the headphones isn’t for me. I found them uncomfortable to wear for long periods of time, and the cheap foam padding made this even worse. To find out more about the headphones, you can keep reading this full Grado SR325 review.</p><h2 class="article-body__section" id="section-grado-sr325-review-specs"><span>Grado SR325 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/GRADO-Classic-SR325-Open-Back-Headphones/dp/B0GTN8J2VW" target="_blank" rel="nofollow">$350</a> / <a href="https://www.grado.co.uk/classic-series/sr325-classic.html" target="_blank" rel="nofollow">£349</a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Open back, on-ear</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>44mm dynamic </p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>18-24 kHz</p></td></tr><tr><td class="firstcol " ><p><strong>Impdance</strong></p></td><td  ><p>38 ohms</p></td></tr><tr><td class="firstcol " ><p><strong>SPL 1mW</strong></p></td><td  ><p>98</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Wired, 3.5mm jack with 6.3mm adapter</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>14 ounces</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-grado-sr325-review-price-availability"><span>Grado SR325 review: Price & availability</span></h2><p>The Grado SR325 headphones are available in the U.S. on <a href="https://www.grado.co.uk/classic-series/sr325-classic.html" target="_blank" rel="nofollow">Amazon for $350</a> and in the U.K. on the <a href="https://www.grado.co.uk/classic-series/sr325-classic.html" target="_blank" rel="nofollow">Grado website for £349.</a> When it comes to open-back headphones, you’re likely to be forking out a fair bit of cash for great sound, so that’s to be expected.</p><p>For around the same price, you can pick up the <a href="https://www.tomsguide.com/audio/over-ear-headphones/fiio-ft13-review">FiiO FT13</a> ($329). These cans provide a deep, rich sound with impeccable instrument separation. Similarly, the <a href="https://www.tomsguide.com/audio/over-ear-headphones/meze-99-classics-2nd-gen-review">Meze 99 Classics Gen 2</a>, which cost $349, can provide an even cleaner sound. The Mezes are over-ear headphones with a light headband, making them comfortable to wear for long periods, unlike the Grados. </p><p>The <a href="https://www.tomsguide.com/audio/i-just-tested-these-wired-headphones-and-they-sound-absolutely-stunning-without-breaking-the-bank">Meze Audio 105 AER</a> are another solid choice, offering warm sound, some of the best clarity you’ll find, and much better comfort. That’s all for $299, which makes them a true no-brainer when compared with the questionable sound and $350 price tag of the Grado SR325.</p><h2 class="article-body__section" id="section-grado-sr325-review-design"><span>Grado SR325 review: Design </span></h2><p>The Grado SR325 are on-ear headphones with an open-back design. They have built-in wires, so there's no need to plug in each time you listen. The headphones look sleek with black aluminum details and a faux leather headband. I think they look great and more than a little retro, but the headband feels flimsy as it’s not very thick. The headphones don’t fold and don’t come with a case either, so you’d need to invest in a headphone stand to ensure they stay in tip-top condition.</p><h3 id="comfort-5">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gofZbkR2SAi6UDJJEuDEbP" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/gofZbkR2SAi6UDJJEuDEbP.jpg" 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>While the Grado SR325 are comfortable to wear for short listening sessions, I would much rather use a pair of over-ear headphones like the <a href="https://www.tomsguide.com/audio/over-ear-headphones/fiio-ft13-review">FiiO FT13</a> ($329) for comfort over extended periods of time. The on-ear style Grado headphones rest directly on my ears, making the cartilage ache, which is annoying if, like me, you want to listen to music for hours on end. Also, the cheap, thin foam used to cover the earcups is uncomfortable and a little bit scratchy. It reminds me of the foam covering on the <a href="https://www.tomsguide.com/audio/headphones/gadhouse-wesley-headphones-review">Gadhouse Wesley</a> headphones, which are significantly cheaper at $69. For $350, I expect more care to be taken with the comfort of the Grado cans. </p><p>The headphones weigh 14 ounces and didn’t feel too heavy, but the top of my head became sore where the headband was resting after about 45 minutes because there’s not much padding.</p><h3 id="connectivity-7">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="puyKNQ9wmSNEEo7k83hq8P" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/puyKNQ9wmSNEEo7k83hq8P.jpg" 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 Grado SR325 headphones have built-in wires connected to both earcups that merge into one aux wire. You have the choice between a 3.5mm jack or a 6.3mm adapter when connecting to your device. </p><p>While listening, I used the <a href="https://www.amazon.com/FiiO-K11-Headphone-Amplifier-Balanced/dp/B0D9BCRVXN" target="_blank" rel="nofollow">FiiO K11 R2R</a> DAC to power the headphones, and my colleagues also tested the cans with the iFi hip-dac 3 and FiiO K15. While the SR325 aren’t very hard to drive and can be powered by your laptop or phone, using a good DAC ensures they’ll sound their best, and I could hear a stark difference between listening with and without. </p><h2 class="article-body__section" id="section-grado-sr325-review-sound-quality"><span>Grado SR325 review: Sound quality</span></h2><p>The Grado SR325 headphones have a very interesting sound profile. It’s a sound I completely enjoy, and they accentuate different areas from other audiophile headphones I’ve used, but this original sound is fuzzy, adopting an analog tone, like a cup of tea on an autumn afternoon. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="DfRpqBJ28WrtGQQK3urhwN" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/DfRpqBJ28WrtGQQK3urhwN.jpg" 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 listened using <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, one of the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming services</a>, and used the FiiO K11 R2R desktop DAC to amplify the sound. The headphones offer a well-rounded sound across most genres. They thrive with simpler, acoustic music, but they also enhance layers in more complex tracks.</p><p>Firstly, I listened to <em>Music On The Radio</em> by Empire Of The Sun to test the bass performance. While it didn’t deliver the same depth of bass I can expect from my everyday drivers, which are the <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-1000xm6-review">Sony WH-1000XM6</a>, I was able to appreciate all of the layers of the track without the bass becoming crackly or overwhelming. This means the headphones strike a good balance and allow me to hear more of the vocal performance. Bass certainly takes a backseat, but it’s still there, gently warming the track from the bottom up. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZFdk97EHxZsajMisVqps2P" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/ZFdk97EHxZsajMisVqps2P.jpg" 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 out the mid-range performance, I listened to <em>Birds</em> by Turnstile, as it has a prominent guitar riff complemented by heavy drums. The hi-hats stood out between the verse and the chorus, and the guitar riff was incredibly powerful when it kicked in. The soundstage wasn’t as wide as when I listen using my <a href="https://www.tomsguide.com/audio/headphones/beyerdynamic-dt-270-pro-review">Beyerdynamic DT 270 Pro</a>, but the level of detail made up for that. </p><p>Finally, to test the treble performance of the Grados, I listened to <em>The Last Man On Earth </em>by Wolf Alice. This song focuses on the vocal performance before exploding with the addition of harmonies, drums, and bass. The vocals were bright throughout and so detailed that I could hear every breath. When the drums kicked in, I experienced some very slight fuzziness, but that’s why I think these headphones do a better job with simple tracks. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Nyoj7uJq4jvKA4c59SHjaP" name="Grado SR325" alt="Grado SR325 photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/Nyoj7uJq4jvKA4c59SHjaP.jpg" 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>Overall, I think the Grado SR325 headphones are a great option for detailed listening, especially for people who like detailed tracks.</p><h2 class="article-body__section" id="section-grado-sr325-review-verdict"><span>Grado SR325 review: Verdict</span></h2><p>The Grado SR325 headphones have a sleek-looking, retro design that is sure to look great next to any audio setup. My favorite thing about these cans is the amazing vocal replication. The mid-range is also exceptionally detailed, with guitar riffs taking the forefront where needed. The warm sound makes these cans very pleasant to listen to. </p><p>And while the sleek, retro design looks good, I can’t help but notice that the foam padding on the earcups feels very cheap. This doesn’t help that the on-ear style is uncomfortable for long listening periods. </p><p>Overall, these headphones are a great choice, but for $350, you may be expecting a little more comfort with your sound. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Beyerdynamic DT 30 IE are some of the most comfortable in-ear monitors I’ve tested — but they aren’t for everyone ]]></title>
                                                                                                <dc:content><![CDATA[ <p>If you want to stream hi-res audio without spending a small fortune, the Beyerdynamic DT 30 IE are worth a look. There’s a lot to like about these in-ear monitors. They’re very light and extremely comfortable for all-day wear, and they’re IP54-rated to withstand splashes and sweat. In addition to that, the DT 30 IE are good for gigging musicians as they boast excellent instrument separation and tight, meaty bass.</p><p>But they aren’t perfect. Unfortunately, the DT 30 IE suffer from an all-too-plasticky feel. While the bass is commendable, the mids and treble could have been more refined and textured. And given that they face fierce competition at their price point, do they do just enough to edge out their rivals?</p><p>For the complete breakdown, and to find out if these are the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best wired headphones </a>for you, read my full Beyerdynamic DT 30 IE review.</p><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-specs"><span>Beyerdynamic DT 30 IE 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/beyerdynamic-DT-30-Professional-Dust-Resistant/dp/B0GXVL4X5G" target="_blank" rel="nofollow">$159</a> / <a href="https://www.amazon.co.uk/beyerdynamic-DT-30-Professional-Dust-Resistant-Clear/dp/B0GXVL4X5G" target="_blank" rel="nofollow">£99</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>Single 11mm dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>5Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>3.5mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>Wired</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>0.09oz (each earpiece)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Transparent white</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-price-availability"><span>Beyerdynamic DT 30 IE 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="fxC7eK2EVTKPigunrBkqkd" name="BeyerDynamics_DT30_IE_Monitor_Earphones_006.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/fxC7eK2EVTKPigunrBkqkd.jpg" 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 Beyerdynamic DT 30 IE in-ear monitors (IEMs) enable you to stream hi-res audio without breaking the bank. Launched in June 2026, they have a retail price of <a href="https://www.amazon.com/beyerdynamic-DT-30-Professional-Dust-Resistant/dp/B0GXVL4X5G" target="_blank" rel="nofollow">$159</a> / <a href="https://www.amazon.co.uk/beyerdynamic-DT-30-Professional-Dust-Resistant-Clear/dp/B0GXVL4X5G" target="_blank" rel="nofollow">£99 at Amazon</a>.</p><p>They’re priced similarly to the <a href="https://www.tomsguide.com/audio/earbuds/sivga-lyrebird-iems-review">Sivga Lyrebird</a> ($149), the <a href="https://www.tomsguide.com/audio/earbuds/sennheiser-ie-200-wired-earbuds-review">Sennheiser IE 200</a> ($149) and the gaming-first <a href="https://www.tomsguide.com/gaming/gaming-peripherals/sony-inzone-e9-gaming-iems-review">Sony Inzone E9</a> ($149). As you’ll soon see, the DT 30 IE are good for some people but not for everyone — and it might make more sense to buy any of the aforementioned alternatives if you fall into the latter category.</p><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-design"><span>Beyerdynamic DT 30 IE 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="ujboJ5zix5Rwe4vTLar3xb" name="BeyerDynamics_DT30_IE_Monitor_Earphones_012.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/ujboJ5zix5Rwe4vTLar3xb.jpg" 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 Beyerdynamic DT 30 IE are good looking, with each earpiece fitted with a transparent shell that lets you see the IEMs’ inner workings or hardware. They don’t look and feel as premium as the Sivga Lyrebird, though, which feature wooden faceplates and CNC-machined aluminum housing. The DT 30 IE feel quite plasticky, and I’d recommend the Lyrebird if that’s a dealbreaker.</p><p>Also unlike the Lyrebird, the DT 30 IE don’t feature a specialized anti-tangle cable — but to ensure they don’t get bunched up in your pocket, Beyerdynamic happily includes a carry case for the IEMs. Regardless, the DT 30 IE’s 1.4m Kevlar-reinforced cable feels sturdy and durable. It’s detachable from the ear pieces, too, which means that you can use a different MMCX cable if the stock one gets damaged.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kiBdbLTCixnrGGDmTZrAkd" name="BeyerDynamics_DT30_IE_Monitor_Earphones_016.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/kiBdbLTCixnrGGDmTZrAkd.jpg" 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>Similar to the Sennheiser IE 200 and the Sivga Lyrebird, you wrap the DT 30 IE’s cables around your ears to ensure they don’t fall off. I found that the cable stays the shape you mould it into, for a secure fit, and the IEMs didn’t fall out of my ears. To ensure a perfect fit, Beyerdynamic includes six pairs of eartips, of varying sizes, in the box: three silicone for a balanced seal, and three foam for more comfort (and some degree of passive noise cancelation).</p><p>Replacing the tips on the ear pieces is a walk in the park too — unlike on the budget <a href="https://www.tomsguide.com/audio/earbuds/dunu-titan-x-iems-review">Dunu Titan X</a> ($39) where it took me 30 minutes to do so. On the DT 30 IE, it took me just under a minute to pull off the eartips and replace them. Quick, easy and painless.</p><h2 id="weatherproofing-4">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7ioYhysn6Fos4KWvLuBcob" name="BeyerDynamics_DT30_IE_Monitor_Earphones_009.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/7ioYhysn6Fos4KWvLuBcob.jpg" 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>Wired earbuds and IEMs are rarely weatherproof, but the DT 30 IE are. Unlike the Sennheiser IE 200 and the Sivga Lyrebird, the DT 30 IE carry an IP54 rating, which means they’re resistant to dust and water splashes. Beyerdynamic says that this makes the DT 30 IE suitable for use by musicians during sweat-inducing and intense concerts. You should be fine wearing them to the gym.</p><h2 id="comfort-6">Comfort</h2><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="bCYiJVe5KHkSd4ydQGzKnj" name="Beyerdynamic_DT30_IE" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/bCYiJVe5KHkSd4ydQGzKnj.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: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>The DT 30 IE are ridiculously comfortable IEMs, thanks to each ear piece weighing just 0.09oz. I’ve worn them for four straight hours without any discomfort. The DT 30 IE are lighter than all of their $149 competitors, like the Sivga Lyrebird (0.22oz each), the Sennheiser IE 200 (0.14oz each), and the Sony Inzone E9 (0.16oz each).</p><h2 id="controls-8">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="p2T9EKmSFyKcfvQXdw8yid" name="BeyerDynamics_DT30_IE_Monitor_Earphones_004.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/p2T9EKmSFyKcfvQXdw8yid.jpg" 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>Similar to other IEMs I’ve tested and studio monitors designed for music professionals, the DT 30 IE don’t feature any in-line buttons to control playback, so you’ll need to use your audio source for changing tracks.</p><h2 id="connectivity-amp-drive-2">Connectivity & drive</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ANkHws4Z78wvA3WuhJKcjd" name="BeyerDynamics_DT30_IE_Monitor_Earphones_007.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/ANkHws4Z78wvA3WuhJKcjd.jpg" 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 DT 30 IE are wired IEMs, so you don’t get Bluetooth or 2.4GHz connectivity as you do with the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a>. This means that they need to be powered, or driven, by a power source. They come fitted with a 3.5mm plug that you can use with your phone, laptop, or a DAC/Amp. If you want to use them with a mixing console or bodypack system, like a mic transmitter, you’ll need to buy a dedicated 3.5mm to 6.35mm adapter separately.</p><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-sound-quality"><span>Beyerdynamic DT 30 IE 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="cYojxp2JayWUbMmYEoKNid" name="BeyerDynamics_DT30_IE_Monitor_Earphones_010.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/cYojxp2JayWUbMmYEoKNid.jpg" 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 Beyerdynamic DT 30 IE feature a single 11mm dynamic driver setup and they cover a frequency response range of 5Hz to 20KHz. For the most part, music sounds excellent through the DT 30 IE, and these IEMs reproduce most sounds accurately. I tested them by listening to my usual mix of testing songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles.</p><p>We’ll kick things off with what the DT 30 IE do well. Basslines through the DT 30 IE sound powerful but controlled, and that was the first thing I noticed when listening to ‘<a href="https://open.qobuz.com/track/398890244" target="_blank" rel="nofollow">2515</a>’ by Wasia Project. The track’s dark electronic textures felt like they had a tangible weight to them, while the bass and kick drum packed a convincing punch. The song sounded powerful without becoming muddy. Each note stopped and started cleanly, remaining disciplined, while retaining its texture.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Rnt9EGxGnARkBcVJ7eKLhd" name="BeyerDynamics_DT30_IE_Monitor_Earphones_013.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/Rnt9EGxGnARkBcVJ7eKLhd.jpg" 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>Pink Floyd’s ‘<a href="https://open.qobuz.com/track/47683556" target="_blank" rel="nofollow">Money</a>’ showed off the DT 30 IE’s great instrument and layer separation. Each clink and mechanical sound in the opening cash register loop remained distinct rather than blending together. All the instruments occupied their own spaces, such as Roger Waters’ iconic bassline and the melodious saxophone, but they gelled well together to deliver an immersive listening experience.</p><p>I then listened to ‘<a href="https://open.qobuz.com/track/203578466" target="_blank" rel="nofollow">A&W</a>’ by Lana Del Rey to test the IEMs’ mid-range. The DT 30 IE handle the abrupt shift between low-end heavy beats and Lana’s textured vocals well, with the transitions sounding seamless. I appreciated being able to hear the whispered words and easy-to-miss ad libs. However, I wasn’t entirely convinced by the way the DT 30 IE reproduced the upper mids. The vocals in the mid-range sounded flat and lacked body.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uGvDRqiJM26FKGCqi8KLid" name="BeyerDynamics_DT30_IE_Monitor_Earphones_017.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/uGvDRqiJM26FKGCqi8KLid.jpg" 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>Similarly, I wasn’t a massive fan of the treble. In Yin Yin’s ‘<a href="https://open.qobuz.com/track/361933224" target="_blank" rel="nofollow">Elma</a>,’ the percussion, cymbals and higher-pitched instrumental details were present, yes, but they lacked the razor-sharp quality and sheen I experienced when listening to the same track through the Sivga Lyrebird. The track almost sounded smooth around the edges when in reality, it’s energetic and sparkly.</p><p>If you want a pair of IEMs that boasts powerful bass and good instrument separation, the DT 30 IE are sure to please. But if you want a pair that doesn’t compromise on every other part of the soundstage, I’d recommend the Sivga Lyrebird or the Sennheiser IE 200.</p><p><em>Listen along to my review with the Beyerdynamic DT 30 IE testing playlist:</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/69119300?zone=GB-en"></iframe><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-verdict"><span>Beyerdynamic DT 30 IE 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="GscqyeKw9GuZpb5oKQ5jse" name="BeyerDynamics_DT30_IE_Monitor_Earphones_015.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/GscqyeKw9GuZpb5oKQ5jse.jpg" 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 Beyerdynamic DT 30 IE are some of the most comfortable in-ear monitors I’ve had the pleasure of testing. They’re so light that you forget you’re wearing them, and they’re IP54-rated which makes them more versatile than some of their competitors. There’s plenty to enjoy sound-wise, too, as the DT 30 IE deliver punchy bass and excellent instrument separation.</p><p>However, the mid-range and highs sound a little flat and one-dimensional, which makes the Sivga Lyrebird better value for money. The ear pieces have a plasticky feel and look, too, which may not be everyone’s cup of tea.</p><p>The DT 30 IE are a solid choice if bass and comfort are your priorities, but if you want a more balanced, detailed sound, or a more eye-catching design, there are better similarly-priced options available.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/beyerdynamic-dt-30-ie-iems-review</link>
                                                                            <description>
                            <![CDATA[ The Beyerdynamic DT 30 IE are comfortable in-ear monitors that boast excellent bass and instrument separation. However, the treble and mids sound flat. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">WtRCP7b6mkJotbCNEJLBNJ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/JD4PGqyzUmvPCAYezSrjYj-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 30 Aug 2026 10:45: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.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/JD4PGqyzUmvPCAYezSrjYj-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Beyerdynamic DT 30 IE in-ear monitors]]></media:description>                                                            <media:text><![CDATA[Beyerdynamic DT 30 IE in-ear monitors]]></media:text>
                                <media:title type="plain"><![CDATA[Beyerdynamic DT 30 IE in-ear monitors]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/JD4PGqyzUmvPCAYezSrjYj-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you want to stream hi-res audio without spending a small fortune, the Beyerdynamic DT 30 IE are worth a look. There’s a lot to like about these in-ear monitors. They’re very light and extremely comfortable for all-day wear, and they’re IP54-rated to withstand splashes and sweat. In addition to that, the DT 30 IE are good for gigging musicians as they boast excellent instrument separation and tight, meaty bass.</p><p>But they aren’t perfect. Unfortunately, the DT 30 IE suffer from an all-too-plasticky feel. While the bass is commendable, the mids and treble could have been more refined and textured. And given that they face fierce competition at their price point, do they do just enough to edge out their rivals?</p><p>For the complete breakdown, and to find out if these are the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best wired headphones </a>for you, read my full Beyerdynamic DT 30 IE review.</p><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-specs"><span>Beyerdynamic DT 30 IE 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/beyerdynamic-DT-30-Professional-Dust-Resistant/dp/B0GXVL4X5G" target="_blank" rel="nofollow">$159</a> / <a href="https://www.amazon.co.uk/beyerdynamic-DT-30-Professional-Dust-Resistant-Clear/dp/B0GXVL4X5G" target="_blank" rel="nofollow">£99</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>Single 11mm dynamic</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>5Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>3.5mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>Wired</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>0.09oz (each earpiece)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Transparent white</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-price-availability"><span>Beyerdynamic DT 30 IE 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="fxC7eK2EVTKPigunrBkqkd" name="BeyerDynamics_DT30_IE_Monitor_Earphones_006.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/fxC7eK2EVTKPigunrBkqkd.jpg" 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 Beyerdynamic DT 30 IE in-ear monitors (IEMs) enable you to stream hi-res audio without breaking the bank. Launched in June 2026, they have a retail price of <a href="https://www.amazon.com/beyerdynamic-DT-30-Professional-Dust-Resistant/dp/B0GXVL4X5G" target="_blank" rel="nofollow">$159</a> / <a href="https://www.amazon.co.uk/beyerdynamic-DT-30-Professional-Dust-Resistant-Clear/dp/B0GXVL4X5G" target="_blank" rel="nofollow">£99 at Amazon</a>.</p><p>They’re priced similarly to the <a href="https://www.tomsguide.com/audio/earbuds/sivga-lyrebird-iems-review">Sivga Lyrebird</a> ($149), the <a href="https://www.tomsguide.com/audio/earbuds/sennheiser-ie-200-wired-earbuds-review">Sennheiser IE 200</a> ($149) and the gaming-first <a href="https://www.tomsguide.com/gaming/gaming-peripherals/sony-inzone-e9-gaming-iems-review">Sony Inzone E9</a> ($149). As you’ll soon see, the DT 30 IE are good for some people but not for everyone — and it might make more sense to buy any of the aforementioned alternatives if you fall into the latter category.</p><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-design"><span>Beyerdynamic DT 30 IE 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="ujboJ5zix5Rwe4vTLar3xb" name="BeyerDynamics_DT30_IE_Monitor_Earphones_012.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/ujboJ5zix5Rwe4vTLar3xb.jpg" 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 Beyerdynamic DT 30 IE are good looking, with each earpiece fitted with a transparent shell that lets you see the IEMs’ inner workings or hardware. They don’t look and feel as premium as the Sivga Lyrebird, though, which feature wooden faceplates and CNC-machined aluminum housing. The DT 30 IE feel quite plasticky, and I’d recommend the Lyrebird if that’s a dealbreaker.</p><p>Also unlike the Lyrebird, the DT 30 IE don’t feature a specialized anti-tangle cable — but to ensure they don’t get bunched up in your pocket, Beyerdynamic happily includes a carry case for the IEMs. Regardless, the DT 30 IE’s 1.4m Kevlar-reinforced cable feels sturdy and durable. It’s detachable from the ear pieces, too, which means that you can use a different MMCX cable if the stock one gets damaged.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kiBdbLTCixnrGGDmTZrAkd" name="BeyerDynamics_DT30_IE_Monitor_Earphones_016.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/kiBdbLTCixnrGGDmTZrAkd.jpg" 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>Similar to the Sennheiser IE 200 and the Sivga Lyrebird, you wrap the DT 30 IE’s cables around your ears to ensure they don’t fall off. I found that the cable stays the shape you mould it into, for a secure fit, and the IEMs didn’t fall out of my ears. To ensure a perfect fit, Beyerdynamic includes six pairs of eartips, of varying sizes, in the box: three silicone for a balanced seal, and three foam for more comfort (and some degree of passive noise cancelation).</p><p>Replacing the tips on the ear pieces is a walk in the park too — unlike on the budget <a href="https://www.tomsguide.com/audio/earbuds/dunu-titan-x-iems-review">Dunu Titan X</a> ($39) where it took me 30 minutes to do so. On the DT 30 IE, it took me just under a minute to pull off the eartips and replace them. Quick, easy and painless.</p><h2 id="weatherproofing-4">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7ioYhysn6Fos4KWvLuBcob" name="BeyerDynamics_DT30_IE_Monitor_Earphones_009.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/7ioYhysn6Fos4KWvLuBcob.jpg" 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>Wired earbuds and IEMs are rarely weatherproof, but the DT 30 IE are. Unlike the Sennheiser IE 200 and the Sivga Lyrebird, the DT 30 IE carry an IP54 rating, which means they’re resistant to dust and water splashes. Beyerdynamic says that this makes the DT 30 IE suitable for use by musicians during sweat-inducing and intense concerts. You should be fine wearing them to the gym.</p><h2 id="comfort-6">Comfort</h2><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="bCYiJVe5KHkSd4ydQGzKnj" name="Beyerdynamic_DT30_IE" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/bCYiJVe5KHkSd4ydQGzKnj.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: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>The DT 30 IE are ridiculously comfortable IEMs, thanks to each ear piece weighing just 0.09oz. I’ve worn them for four straight hours without any discomfort. The DT 30 IE are lighter than all of their $149 competitors, like the Sivga Lyrebird (0.22oz each), the Sennheiser IE 200 (0.14oz each), and the Sony Inzone E9 (0.16oz each).</p><h2 id="controls-8">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="p2T9EKmSFyKcfvQXdw8yid" name="BeyerDynamics_DT30_IE_Monitor_Earphones_004.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/p2T9EKmSFyKcfvQXdw8yid.jpg" 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>Similar to other IEMs I’ve tested and studio monitors designed for music professionals, the DT 30 IE don’t feature any in-line buttons to control playback, so you’ll need to use your audio source for changing tracks.</p><h2 id="connectivity-amp-drive-2">Connectivity & drive</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ANkHws4Z78wvA3WuhJKcjd" name="BeyerDynamics_DT30_IE_Monitor_Earphones_007.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/ANkHws4Z78wvA3WuhJKcjd.jpg" 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 DT 30 IE are wired IEMs, so you don’t get Bluetooth or 2.4GHz connectivity as you do with the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a>. This means that they need to be powered, or driven, by a power source. They come fitted with a 3.5mm plug that you can use with your phone, laptop, or a DAC/Amp. If you want to use them with a mixing console or bodypack system, like a mic transmitter, you’ll need to buy a dedicated 3.5mm to 6.35mm adapter separately.</p><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-sound-quality"><span>Beyerdynamic DT 30 IE 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="cYojxp2JayWUbMmYEoKNid" name="BeyerDynamics_DT30_IE_Monitor_Earphones_010.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/cYojxp2JayWUbMmYEoKNid.jpg" 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 Beyerdynamic DT 30 IE feature a single 11mm dynamic driver setup and they cover a frequency response range of 5Hz to 20KHz. For the most part, music sounds excellent through the DT 30 IE, and these IEMs reproduce most sounds accurately. I tested them by listening to my usual mix of testing songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles.</p><p>We’ll kick things off with what the DT 30 IE do well. Basslines through the DT 30 IE sound powerful but controlled, and that was the first thing I noticed when listening to ‘<a href="https://open.qobuz.com/track/398890244" target="_blank" rel="nofollow">2515</a>’ by Wasia Project. The track’s dark electronic textures felt like they had a tangible weight to them, while the bass and kick drum packed a convincing punch. The song sounded powerful without becoming muddy. Each note stopped and started cleanly, remaining disciplined, while retaining its texture.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Rnt9EGxGnARkBcVJ7eKLhd" name="BeyerDynamics_DT30_IE_Monitor_Earphones_013.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/Rnt9EGxGnARkBcVJ7eKLhd.jpg" 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>Pink Floyd’s ‘<a href="https://open.qobuz.com/track/47683556" target="_blank" rel="nofollow">Money</a>’ showed off the DT 30 IE’s great instrument and layer separation. Each clink and mechanical sound in the opening cash register loop remained distinct rather than blending together. All the instruments occupied their own spaces, such as Roger Waters’ iconic bassline and the melodious saxophone, but they gelled well together to deliver an immersive listening experience.</p><p>I then listened to ‘<a href="https://open.qobuz.com/track/203578466" target="_blank" rel="nofollow">A&W</a>’ by Lana Del Rey to test the IEMs’ mid-range. The DT 30 IE handle the abrupt shift between low-end heavy beats and Lana’s textured vocals well, with the transitions sounding seamless. I appreciated being able to hear the whispered words and easy-to-miss ad libs. However, I wasn’t entirely convinced by the way the DT 30 IE reproduced the upper mids. The vocals in the mid-range sounded flat and lacked body.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uGvDRqiJM26FKGCqi8KLid" name="BeyerDynamics_DT30_IE_Monitor_Earphones_017.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/uGvDRqiJM26FKGCqi8KLid.jpg" 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>Similarly, I wasn’t a massive fan of the treble. In Yin Yin’s ‘<a href="https://open.qobuz.com/track/361933224" target="_blank" rel="nofollow">Elma</a>,’ the percussion, cymbals and higher-pitched instrumental details were present, yes, but they lacked the razor-sharp quality and sheen I experienced when listening to the same track through the Sivga Lyrebird. The track almost sounded smooth around the edges when in reality, it’s energetic and sparkly.</p><p>If you want a pair of IEMs that boasts powerful bass and good instrument separation, the DT 30 IE are sure to please. But if you want a pair that doesn’t compromise on every other part of the soundstage, I’d recommend the Sivga Lyrebird or the Sennheiser IE 200.</p><p><em>Listen along to my review with the Beyerdynamic DT 30 IE testing playlist:</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/69119300?zone=GB-en"></iframe><h2 class="article-body__section" id="section-beyerdynamic-dt-30-ie-review-verdict"><span>Beyerdynamic DT 30 IE 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="GscqyeKw9GuZpb5oKQ5jse" name="BeyerDynamics_DT30_IE_Monitor_Earphones_015.JPG" alt="Beyerdynamic DT 30 IE in-ear monitors" src="https://cdn.mos.cms.futurecdn.net/GscqyeKw9GuZpb5oKQ5jse.jpg" 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 Beyerdynamic DT 30 IE are some of the most comfortable in-ear monitors I’ve had the pleasure of testing. They’re so light that you forget you’re wearing them, and they’re IP54-rated which makes them more versatile than some of their competitors. There’s plenty to enjoy sound-wise, too, as the DT 30 IE deliver punchy bass and excellent instrument separation.</p><p>However, the mid-range and highs sound a little flat and one-dimensional, which makes the Sivga Lyrebird better value for money. The ear pieces have a plasticky feel and look, too, which may not be everyone’s cup of tea.</p><p>The DT 30 IE are a solid choice if bass and comfort are your priorities, but if you want a more balanced, detailed sound, or a more eye-catching design, there are better similarly-priced options available.</p>
                                                            </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.jpg" 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.jpg" 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-9">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.jpg" 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.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.jpg" 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.jpg" 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.jpg" 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.jpg" 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.jpg" 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/NaMzNekhTVBsYtbsSfJh6E-1280-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.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/NaMzNekhTVBsYtbsSfJh6E-1280-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/NaMzNekhTVBsYtbsSfJh6E-1280-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.jpg" 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.jpg" 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-9">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.jpg" 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.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.jpg" 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.jpg" 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.jpg" 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.jpg" 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.jpg" 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[ Before you buy your next pair of speakers, make sure you know this one essential difference ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I wish I had bought myself better speakers growing up. For the better part of my late teens, I used a hand-me-down portable boombox that I used to play burned CDs from friends. It's not like these days where you can just pick up one of the <a href="https://www.tomsguide.com/us/best-bluetooth-speakers,review-1982.html">best Bluetooth speakers</a> and be done with it.</p><p>Still, had I known about better audio products — and the differences between active and passive speakers — I would’ve traded out that boombox in a heartbeat. </p><p>So let’s break down the differences between speakers, starting with the most obvious distinction: How do speakers get power and information to play your favorite music?</p><h2 id="passive-vs-active-speakers-major-differences">Passive vs Active Speakers: Major differences</h2><p>If you’re just encountering these terms for the first time, the words “passive” and “active” might bring to mind two very different-sounding pairs of speakers — one pair that’s loud and aggressive, and another that’s quieter and muffled. </p><div><blockquote><p>Thankfully, 'passive' and 'active' doesn’t refer to how the speakers sound, but rather how their internal components work and whether or not they require external amplification.</p></blockquote></div><p>Thankfully, 'passive' and 'active' doesn’t refer to how the speakers sound, but rather how their internal components work and whether or not they require external amplification. That, in a nutshell, is the biggest difference between the two: Passive speakers require external amplification whereas active speakers come with an amplifier built in. </p><p>When it’s framed like this, you might immediately be drawn to active speakers — they sure sound like a lot less work — and you wouldn’t be wrong. </p><p>Since a manufacturer has done all the hard work of pairing the drivers to an amplifier, you don’t have to spend the time looking at specs sheets to make sure you’re not overdriving or under-driving them. However, by choosing to go with active speakers, you’re missing out on the secret advantage of passive speakers. (Hint: Changing out amplifiers at the drop of a hat is actually a benefit, not a curse.) </p><p>The other major difference is how these speakers actually get the sonic information passed over to them. On active speakers, information is obtained wirelessly, over optical audio or via a set of phono cables. Passive speakers use traditional speaker wire. We’ll get into the pros and cons of each in just a minute.</p><h2 id="active-speakers-pros-cons-and-sound-quality">Active speakers: Pros, cons and sound quality</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D62CdZ8XFamJWG3nuVeXLa" name="Edifier_S880DB_MK2_.JPG" alt="the edifier s880db mkii speakers in white and wood colorway showing the connectivity options, remote control, against a blue background" src="https://cdn.mos.cms.futurecdn.net/D62CdZ8XFamJWG3nuVeXLa.jpg" 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 main advantage of active speakers is that they work right out of the box without any additional hardware. These days, most active speakers come with a number of wireless connectivity options built in like AirPlay, Chromecast Built-in, Spotify and Tidal Connect, alongside many more. </p><p>On the backside of the main speaker (we call this the lead speaker or, in some cases, the master speaker) are a plethora of ports. Here you’ll find your standard phono inputs, usually a sub-out if you want to connect a separate subwoofer, optical in and, if you’re lucky, an HDMI port that allows you to connect the speakers to your TV without an AV receiver.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SV6aDGVKbcC8HF7xKkgnKa" name="Edifier_S880DB_MK2_ 9.JPG" alt="the edifier s880db mkii speakers in white and wood colorway showing the connectivity options, remote control, against a blue background" src="https://cdn.mos.cms.futurecdn.net/SV6aDGVKbcC8HF7xKkgnKa.jpg" 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>Active speakers are connectivity rich and easy to set up, so what’s the downside? The downside is that the speakers will always sound the same — which isn’t bad if you’re the kind of person who likes to set it and forget it. Tinkerers, however, won’t love this. </p><p>Sound quality will vary from speaker to speaker, obviously, and it's hard to give a broad sweeping generalization about the technology. However, if I had to give one, I'd say the majority of active speakers I've heard follow a v-shaped curve with big, bold bass response and sparkling highs. It's not that the mid-band frequencies are lost entirely; I just feel like they're less prominent in the active speakers I've owned.</p><h2 id="passive-speakers-pros-cons-and-sound-quality">Passive speakers: Pros, cons and sound quality</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6240px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="p8goaxn6ErRpXkDsfo57Q9" name="Ruark Sabre-R 16-9" alt="The Ruark Audio Sabre-R bookshelf speakers in charcoal" src="https://cdn.mos.cms.futurecdn.net/p8goaxn6ErRpXkDsfo57Q9.jpg" mos="" align="middle" fullscreen="" width="6240" height="3512" 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 is going to sound like some wine-swilling non-sense, but different components impart different sonic signatures — and that includes speakers themselves. This comes down to a number of factors including the amplification technology, the digital-to-analog conversion and the DSP (digital signal processing), all of which vary from device to device. </p><p>When talking about speakers and their sound signatures, we sometimes shorthand this chain as tuning, and herein lies the true joy of buying passive speakers: You’re able to tinker the tuning to your exact specifications. In the audio world we have plenty of default tunings, but not everyone’s ears are shaped the same, nor are our music preferences the same. Being able to tailor the sound to your liking is a huge benefit for folks with specific preferences.</p><p>The bad news is that not all music components aren’t interchangeable — and it is possible to cause damage to your audio equipment by mismatching it. This is a lot of fun for some folks, and a very stressful situation for others.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5238px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="Dhg5cqfikSKVRNqGEjGMV7" name="Ruark Sabre-R_5" alt="The Ruark Audio Sabre-R bookshelf speakers rear ports" src="https://cdn.mos.cms.futurecdn.net/Dhg5cqfikSKVRNqGEjGMV7.jpg" mos="" align="middle" fullscreen="" width="5238" height="2946" 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 far as sound quality is concerned, it will again vary between speakers and the components used to drive them. The difference between one system and another can be <em>extreme</em>. Still, I've found properly crafted passive systems to be more nuanced than their active counterparts. </p><p>When I listen to my passive speaker setup, I find new aural details I hadn't heard before in my favorite songs. (Billie Eilish's "<em>WHEN WE ALL FALL ASLEEP, WHERE DO WE GO?" </em>is filled with small details that are totally lost on some speaker systems.)</p><iframe allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" height="352" width="100%" id="" style="border-radius:12px" class="position-center" data-lazy-priority="low" data-lazy-src="https://open.spotify.com/embed/album/0S0KGZnfBGSIssfF54WSJh?utm_source=generator&si=4c53ca7c980248d7"></iframe><h2 id="passive-vs-active-speakers-recommended-models">Passive vs Active Speakers: Recommended models</h2><p>If you just want easy, plug-and-play speakers with lots of features built right in, you should go for a pair of active speakers. There are plenty to choose from, but some of my recent favorites are the affordable <a href="https://www.amazon.com/Edifier-S880DB-MKII-Bookshelf-Bluetooth/dp/B0FGNS3KFC/">Edifier S880DB MKII</a> and the pricier — but much more powerful — <a href="https://www.amazon.com/Klipsch-Nines-Powered-Speakers-Walnut/dp/B0GLQNLG39/">Klipsch The Nines II</a>. You can also go for floorstanding models like the <a href="https://www.amazon.com/Fluance-Floorstanding-Amplifier-Listening-Turntable/dp/B0B788RPVS/">Fluance Ai81 Elite</a>.</p><p>If you want to build out a modular system piecemeal to allow for upgrading down the road, then you want some good passive speakers. As mentioned earlier, picking out a pair is a lot harder because it will depend entirely on the receiver or amplifier you decide to pair them with. </p><p>If the idea sounds daunting, you can always buy a system in a box that pairs passive speakers with a receiver for you, like the <a href="https://www.amazon.com/Yamaha-YHT-4970U-5-1-Channel-Theater-System/dp/B0H2FRHKH5/" target="_blank" rel="nofollow">Yamaha YHT-4970U 5.1-Channel Home Theater System</a>. This is an affordable way to get started with home theater audio. </p><p>For music aficionados wanting a good starter package, pick up the <a href="https://www.amazon.com/Bluesound-Powernode-Multi-Room-Resolution-Streaming/dp/B0FHJ4YMDB/" target="_blank" rel="nofollow">Bluesound Powernode</a> and a pair of <a href="https://www.amazon.com/KEF-LS50-Meta-Carbon-Black/dp/B08G1T1CYZ/" target="_blank" rel="nofollow">KEF LS50 Meta</a> speakers. This way you’re getting both the modularity to upgrade down the line and the convenience of a streaming setup in one package.  </p><p>Recommending systems can be difficult as tastes vary so much from person to person, but building the reference system of your dreams is a lifelong pursuit that many folks — myself included — wholeheartedly enjoy.</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/airpods/ios-27-beta-3-adds-a-clever-new-control-for-airpods-adaptive-mode"><strong>Apple's iOS 27 beta 3 update finally introduces a brilliantly clever and streamlined control interface for AirPods Adaptive mode, empowering users to instantly fine-tune ambient noise blending intensity with just a simple tap</strong></a></li><li><a href="https://www.tomsguide.com/audio/smart-speakers/marshall-acton-iv-review"><strong>The Marshall Acton IV won me over with its charming retro design and incredible sound — and it’s earned a permanent spot on my coffee table</strong></a></li><li><a href="https://www.tomsguide.com/audio/smart-speakers/marshall-stanmore-iv-review"><strong>‘Easy on the eyes and ridiculously stunning sound’ — I tested the Marshall Stanmore IV home speaker and it’s coming for Sonos’ crown</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/speakers/before-you-buy-your-next-pair-of-speakers-make-sure-you-know-this-one-essential-difference</link>
                                                                            <description>
                            <![CDATA[ Not sure whether you should buy passive or active speakers? We can help break down the differences. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">zHhEnAknBrP5dZdTM5eaKi</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/XTvp4PbNASrTXakCzLx76h-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 27 Aug 2026 08:15:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Speakers]]></category>
                                                    <category><![CDATA[Audio]]></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.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/XTvp4PbNASrTXakCzLx76h-1280-80.jpg">
                                                            <media:credit><![CDATA[Klipsch/ KEF/ Tom’s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A pair of KEF and Klipsch speakers next to one another.]]></media:description>                                                            <media:text><![CDATA[A pair of KEF and Klipsch speakers next to one another.]]></media:text>
                                <media:title type="plain"><![CDATA[A pair of KEF and Klipsch speakers next to one another.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/XTvp4PbNASrTXakCzLx76h-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I wish I had bought myself better speakers growing up. For the better part of my late teens, I used a hand-me-down portable boombox that I used to play burned CDs from friends. It's not like these days where you can just pick up one of the <a href="https://www.tomsguide.com/us/best-bluetooth-speakers,review-1982.html">best Bluetooth speakers</a> and be done with it.</p><p>Still, had I known about better audio products — and the differences between active and passive speakers — I would’ve traded out that boombox in a heartbeat. </p><p>So let’s break down the differences between speakers, starting with the most obvious distinction: How do speakers get power and information to play your favorite music?</p><h2 id="passive-vs-active-speakers-major-differences">Passive vs Active Speakers: Major differences</h2><p>If you’re just encountering these terms for the first time, the words “passive” and “active” might bring to mind two very different-sounding pairs of speakers — one pair that’s loud and aggressive, and another that’s quieter and muffled. </p><div><blockquote><p>Thankfully, 'passive' and 'active' doesn’t refer to how the speakers sound, but rather how their internal components work and whether or not they require external amplification.</p></blockquote></div><p>Thankfully, 'passive' and 'active' doesn’t refer to how the speakers sound, but rather how their internal components work and whether or not they require external amplification. That, in a nutshell, is the biggest difference between the two: Passive speakers require external amplification whereas active speakers come with an amplifier built in. </p><p>When it’s framed like this, you might immediately be drawn to active speakers — they sure sound like a lot less work — and you wouldn’t be wrong. </p><p>Since a manufacturer has done all the hard work of pairing the drivers to an amplifier, you don’t have to spend the time looking at specs sheets to make sure you’re not overdriving or under-driving them. However, by choosing to go with active speakers, you’re missing out on the secret advantage of passive speakers. (Hint: Changing out amplifiers at the drop of a hat is actually a benefit, not a curse.) </p><p>The other major difference is how these speakers actually get the sonic information passed over to them. On active speakers, information is obtained wirelessly, over optical audio or via a set of phono cables. Passive speakers use traditional speaker wire. We’ll get into the pros and cons of each in just a minute.</p><h2 id="active-speakers-pros-cons-and-sound-quality">Active speakers: Pros, cons and sound quality</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D62CdZ8XFamJWG3nuVeXLa" name="Edifier_S880DB_MK2_.JPG" alt="the edifier s880db mkii speakers in white and wood colorway showing the connectivity options, remote control, against a blue background" src="https://cdn.mos.cms.futurecdn.net/D62CdZ8XFamJWG3nuVeXLa.jpg" 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 main advantage of active speakers is that they work right out of the box without any additional hardware. These days, most active speakers come with a number of wireless connectivity options built in like AirPlay, Chromecast Built-in, Spotify and Tidal Connect, alongside many more. </p><p>On the backside of the main speaker (we call this the lead speaker or, in some cases, the master speaker) are a plethora of ports. Here you’ll find your standard phono inputs, usually a sub-out if you want to connect a separate subwoofer, optical in and, if you’re lucky, an HDMI port that allows you to connect the speakers to your TV without an AV receiver.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SV6aDGVKbcC8HF7xKkgnKa" name="Edifier_S880DB_MK2_ 9.JPG" alt="the edifier s880db mkii speakers in white and wood colorway showing the connectivity options, remote control, against a blue background" src="https://cdn.mos.cms.futurecdn.net/SV6aDGVKbcC8HF7xKkgnKa.jpg" 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>Active speakers are connectivity rich and easy to set up, so what’s the downside? The downside is that the speakers will always sound the same — which isn’t bad if you’re the kind of person who likes to set it and forget it. Tinkerers, however, won’t love this. </p><p>Sound quality will vary from speaker to speaker, obviously, and it's hard to give a broad sweeping generalization about the technology. However, if I had to give one, I'd say the majority of active speakers I've heard follow a v-shaped curve with big, bold bass response and sparkling highs. It's not that the mid-band frequencies are lost entirely; I just feel like they're less prominent in the active speakers I've owned.</p><h2 id="passive-speakers-pros-cons-and-sound-quality">Passive speakers: Pros, cons and sound quality</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6240px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="p8goaxn6ErRpXkDsfo57Q9" name="Ruark Sabre-R 16-9" alt="The Ruark Audio Sabre-R bookshelf speakers in charcoal" src="https://cdn.mos.cms.futurecdn.net/p8goaxn6ErRpXkDsfo57Q9.jpg" mos="" align="middle" fullscreen="" width="6240" height="3512" 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 is going to sound like some wine-swilling non-sense, but different components impart different sonic signatures — and that includes speakers themselves. This comes down to a number of factors including the amplification technology, the digital-to-analog conversion and the DSP (digital signal processing), all of which vary from device to device. </p><p>When talking about speakers and their sound signatures, we sometimes shorthand this chain as tuning, and herein lies the true joy of buying passive speakers: You’re able to tinker the tuning to your exact specifications. In the audio world we have plenty of default tunings, but not everyone’s ears are shaped the same, nor are our music preferences the same. Being able to tailor the sound to your liking is a huge benefit for folks with specific preferences.</p><p>The bad news is that not all music components aren’t interchangeable — and it is possible to cause damage to your audio equipment by mismatching it. This is a lot of fun for some folks, and a very stressful situation for others.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5238px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="Dhg5cqfikSKVRNqGEjGMV7" name="Ruark Sabre-R_5" alt="The Ruark Audio Sabre-R bookshelf speakers rear ports" src="https://cdn.mos.cms.futurecdn.net/Dhg5cqfikSKVRNqGEjGMV7.jpg" mos="" align="middle" fullscreen="" width="5238" height="2946" 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 far as sound quality is concerned, it will again vary between speakers and the components used to drive them. The difference between one system and another can be <em>extreme</em>. Still, I've found properly crafted passive systems to be more nuanced than their active counterparts. </p><p>When I listen to my passive speaker setup, I find new aural details I hadn't heard before in my favorite songs. (Billie Eilish's "<em>WHEN WE ALL FALL ASLEEP, WHERE DO WE GO?" </em>is filled with small details that are totally lost on some speaker systems.)</p><iframe allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" height="352" width="100%" id="" style="border-radius:12px" class="position-center" data-lazy-priority="low" data-lazy-src="https://open.spotify.com/embed/album/0S0KGZnfBGSIssfF54WSJh?utm_source=generator&si=4c53ca7c980248d7"></iframe><h2 id="passive-vs-active-speakers-recommended-models">Passive vs Active Speakers: Recommended models</h2><p>If you just want easy, plug-and-play speakers with lots of features built right in, you should go for a pair of active speakers. There are plenty to choose from, but some of my recent favorites are the affordable <a href="https://www.amazon.com/Edifier-S880DB-MKII-Bookshelf-Bluetooth/dp/B0FGNS3KFC/">Edifier S880DB MKII</a> and the pricier — but much more powerful — <a href="https://www.amazon.com/Klipsch-Nines-Powered-Speakers-Walnut/dp/B0GLQNLG39/">Klipsch The Nines II</a>. You can also go for floorstanding models like the <a href="https://www.amazon.com/Fluance-Floorstanding-Amplifier-Listening-Turntable/dp/B0B788RPVS/">Fluance Ai81 Elite</a>.</p><p>If you want to build out a modular system piecemeal to allow for upgrading down the road, then you want some good passive speakers. As mentioned earlier, picking out a pair is a lot harder because it will depend entirely on the receiver or amplifier you decide to pair them with. </p><p>If the idea sounds daunting, you can always buy a system in a box that pairs passive speakers with a receiver for you, like the <a href="https://www.amazon.com/Yamaha-YHT-4970U-5-1-Channel-Theater-System/dp/B0H2FRHKH5/" target="_blank" rel="nofollow">Yamaha YHT-4970U 5.1-Channel Home Theater System</a>. This is an affordable way to get started with home theater audio. </p><p>For music aficionados wanting a good starter package, pick up the <a href="https://www.amazon.com/Bluesound-Powernode-Multi-Room-Resolution-Streaming/dp/B0FHJ4YMDB/" target="_blank" rel="nofollow">Bluesound Powernode</a> and a pair of <a href="https://www.amazon.com/KEF-LS50-Meta-Carbon-Black/dp/B08G1T1CYZ/" target="_blank" rel="nofollow">KEF LS50 Meta</a> speakers. This way you’re getting both the modularity to upgrade down the line and the convenience of a streaming setup in one package.  </p><p>Recommending systems can be difficult as tastes vary so much from person to person, but building the reference system of your dreams is a lifelong pursuit that many folks — myself included — wholeheartedly enjoy.</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/airpods/ios-27-beta-3-adds-a-clever-new-control-for-airpods-adaptive-mode"><strong>Apple's iOS 27 beta 3 update finally introduces a brilliantly clever and streamlined control interface for AirPods Adaptive mode, empowering users to instantly fine-tune ambient noise blending intensity with just a simple tap</strong></a></li><li><a href="https://www.tomsguide.com/audio/smart-speakers/marshall-acton-iv-review"><strong>The Marshall Acton IV won me over with its charming retro design and incredible sound — and it’s earned a permanent spot on my coffee table</strong></a></li><li><a href="https://www.tomsguide.com/audio/smart-speakers/marshall-stanmore-iv-review"><strong>‘Easy on the eyes and ridiculously stunning sound’ — I tested the Marshall Stanmore IV home speaker and it’s coming for Sonos’ crown</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Amazon raised prices on Echo, Fire TV, and Kindle devices — here's how much more you're going to pay ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Over the weekend, Amazon quietly raised prices for nearly all of the devices the company makes, including the <a href="https://www.tomsguide.com/us/amazon-echo-dot-gen-3,review-5811.html">Echo Dot</a>, <a href="https://www.tomsguide.com/tablets/e-readers/kindle-paperwhite-2024-review">Kindle Paperwhite</a> and<a href="https://www.tomsguide.com/entertainment/streaming-devices/amazon-launches-updated-fire-tv-stick-hd-thats-slimmer-and-doesnt-require-a-wall-adapter"> Fire TV Stick HD</a>. It's the latest in a series of price increases that have hit nearly every company. You can guess what's to blame.</p><p>Amazon's new prices vary in amount, with at least one device seeing a 60% increase; the company cited the ongoing <a href="https://www.tomsguide.com/computing/the-ram-crisis-is-so-much-bigger-than-pcs-heres-the-rest-of-the-products-caught-in-the-crossfire">RAM crisis</a> and component inflation. First spotted by <a href="https://fortune.com/2026/08/21/exclusive-amazon-quietly-hiked-prices-echo-fire-tv-kindle-eero-significant-increases-memory-costs/" target="_blank">Fortune</a>, some of the company's cheaper devices appear to be the most affected. </p><p>Here's what the new prices look like:</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="FYUSTSLJG4LM47i68vjPdg" name="Echo Show 21-5" alt="Watching Fire TV on the Echo Show 21 in the kitchen" src="https://cdn.mos.cms.futurecdn.net/FYUSTSLJG4LM47i68vjPdg.jpg" mos="" align="middle" fullscreen="" width="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><div ><table><caption>Smart speakers and displays</caption><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>Original price</strong></p></th><th  ><p><strong>New price</strong></p></th><th  ><p><strong>Increase</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Echo Dot</strong></p></td><td  ><p>$49</p></td><td  ><p>$79</p></td><td  ><p>61.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Dot Max</strong></p></td><td  ><p>$99</p></td><td  ><p>$119</p></td><td  ><p>20.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Studio</strong></p></td><td  ><p>$219</p></td><td  ><p>$219</p></td><td  ><p>0%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 5</strong></p></td><td  ><p>$89</p></td><td  ><p>$99</p></td><td  ><p>11.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 8</strong></p></td><td  ><p>$179</p></td><td  ><p>$199</p></td><td  ><p>11.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 11</strong></p></td><td  ><p>$219</p></td><td  ><p>$249</p></td><td  ><p>13.7%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 15</strong></p></td><td  ><p>$299</p></td><td  ><p>$349</p></td><td  ><p>16.7%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 21</strong></p></td><td  ><p>$399</p></td><td  ><p>$499</p></td><td  ><p>25.1%</p></td></tr></tbody></table></div><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Bqve9zMXpkbhMu5Ex5nCpb" name="Amazon Fire TV stick 4K max" alt="Amazon Fire TV stick 4K max" src="https://cdn.mos.cms.futurecdn.net/Bqve9zMXpkbhMu5Ex5nCpb.jpg" mos="" align="middle" fullscreen="" width="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><div ><table><caption>Fire TV sticks</caption><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>Original Price</strong></p></th><th  ><p><strong>New Price</strong></p></th><th  ><p><strong>Increase</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Amazon Fire TV Stick HD</strong></p></td><td  ><p>$35</p></td><td  ><p>$39</p></td><td  ><p>11.4%</p></td></tr><tr><td class="firstcol " ><p><strong>Fire TV Stick 4K Select</strong></p></td><td  ><p>$39</p></td><td  ><p>$49</p></td><td  ><p>25.6%</p></td></tr><tr><td class="firstcol " ><p><strong>Fire TV Stick 4K Max</strong></p></td><td  ><p>$59</p></td><td  ><p>$84</p></td><td  ><p>42.4%</p></td></tr></tbody></table></div><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KVyvXAxLs7HEWJvCgxxEmM" name="Amazon Kindle Colorsoft-6.jpg" alt="Kindle Colorsoft" src="https://cdn.mos.cms.futurecdn.net/KVyvXAxLs7HEWJvCgxxEmM.jpg" mos="" align="middle" fullscreen="" width="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><div ><table><caption>Kindles</caption><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>Original Price</strong></p></th><th  ><p><strong>New Price</strong></p></th><th  ><p><strong>Increase</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Kindle</strong></p></td><td  ><p>$110</p></td><td  ><p>$149</p></td><td  ><p>35.5%</p></td></tr><tr><td class="firstcol " ><p><strong>Kindle Paperwhite</strong></p></td><td  ><p>$159</p></td><td  ><p>$199</p></td><td  ><p>25.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Kindle</strong> <strong>Colorsoft</strong></p></td><td  ><p>$249</p></td><td  ><p>$289</p></td><td  ><p>16.1%</p></td></tr></tbody></table></div><p>The Echo Dot saw the largest increase up 60% and $30, from $49 to $79, while the higher-tier Echo Show 11 is also getting a $30 hike from $219 to $249.</p><p>Additionally, the 16GB Kindle and Kindle Paperwhite saw hikes, going from $109 to $149 and $159 to $199, respectively. The Fire TV Stick HD only saw a $5 hike from $34 to $39 and the 4K Max rose from $59 to $84. </p><p>Surprisingly, the Amazon-owned <a href="https://www.tomsguide.com/face-off/ring-video-doorbell-3-vs-doorbell-pro">Ring lineup of doorbells and security cameras</a> didn't get more expensive. </p><p>An Amazon spokesperson told Tom's Guide, "After absorbing these increases for as long as we could, we recently adjusted pricing across our product lines." She noted that "significant" increases in memory and component costs led to the decision.</p><h2 id="still-a-cheaper-option">Still a cheaper option</h2><p>In some cases, despite the price hikes, Amazon's devices might still be the cheaper option. Unfortunately, that's because everything else also got more expensive this year. </p><p>In July, <a href="https://www.tomsguide.com/entertainment/streaming-devices/some-streaming-sticks-are-doubling-in-price-heres-whats-driving-up-costs">Roku bumped prices</a> on its streaming devices, with the Roku hitting $150 and the entry-level Roku Streaming Stick now at $40, up from $30.</p><p>Kindles might be more expensive than similar rival options like the Nook GlowLight 4, which comes with 32GB of memory for $149. </p><p>The Echo Dot, though, remains cheaper than the <a href="https://www.tomsguide.com/audio/smart-speakers/google-home-speaker-review">Google Home Speaker</a>, which starts at $99, though the gap is only $20 now. </p><h2 id="amazon-devices-outlook">Amazon Devices: 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="g4rv5xpNBNaytDaHXvb2nM" name="Echo Show 8 (4th Gen)--3" alt="The Echo Show 8 (4th Gen) with the Alexa+ logo on screen on a desk" src="https://cdn.mos.cms.futurecdn.net/g4rv5xpNBNaytDaHXvb2nM.jpg" mos="" align="middle" fullscreen="" width="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>At the moment, only the Fire tablets seem to be unaffected — in fact, they’re the only devices currently on sale, but this could be due largely to the fact that it’s <a href="https://www.tomsguide.com/news/ultimate-back-to-school-guide">back to school season</a>. </p><p>For years, Amazon’s Echo smart home speakers have been the company’s trojan horse to get its voice assistant in homes across the world. Given that <a href="https://www.tomsguide.com/ai/alexa-release-date-cost-devices-and-all-the-new-ai-features">Alexa+</a> is available on virtually every Amazon Echo device, the company may have calculated that it’s saturated the market enough so that adoption of Alexa+ wouldn’t be affected by any potential loss in sales.</p><p>Still, the timing is curious, as the company also announced last week that <a href="https://www.aboutamazon.com/news/devices/alexa-plus-fire-tv-free-ai">Alexa+ would be available, free of charge</a>, to its newest Fire TV devices. That means if you have a current-generation Amazon Fire TV Stick, the Fire TV Cube, or Amazon Ember smart TV, you can get the full version of Amazon’s AI-enabled assistant without needing a Prime account, or paying $19.99 per month. </p><p>Regardless of whether you want Alexa+, more than ever it’s worth waiting until Black Friday or Prime Day to purchase one of Amazon’s devices (sadly, the new sale price may be the old regular price). If you don’t want to wait that long, some third-party retailers are still offering the original price. For example, you can still find the Amazon Echo Dot for $49 at <a href="https://www.target.com/p/amazon-echo-dot-5th-gen-2022-smart-speaker-with-alexa-glacier-white/-/A-87130563" target="_blank" rel="nofollow">Target</a> and <a href="https://www.homedepot.com/pep/Amazon-Echo-Dot-5th-Gen-Smart-Speaker-with-Alexa-Deep-Sea-Blue-B09B93ZDG4/323260668" target="_blank" rel="nofollow">Home Depot</a>. </p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><strong></strong><a href="https://www.tomsguide.com/ai/my-12-year-old-noticed-a-missing-amazon-shopping-feature-so-i-tried-to-build-it-with-ai"><strong>My 12-year-old noticed a missing Amazon shopping feature — so I tried to build it with AI</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/sales-events/ive-had-amazon-prime-for-over-a-decade-heres-the-9-best-perks-youre-paying-for-but-probably-not-using"><strong>9 best Amazon Prime perks you're paying for but probably not using</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/sales-events/amazon-has-a-hidden-button-that-connects-you-with-a-real-customer-service-agent-heres-where-to-find-it"><strong>Amazon has a hidden button that connects you with a real customer service agent — here's where to find it</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/smart-speakers/amazon-raised-prices-on-echo-fire-tv-and-kindle-devices-heres-how-much-more-youre-going-to-pay</link>
                                                                            <description>
                            <![CDATA[ This past weekend, Amazon raised prices on most of its devices, including the Echo, Kindle, Fire TV and eero. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">Js4G5GSp5o9jKtzyUmMBcA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/wqVGnqkQWWiAV4PhBUSR8-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 24 Aug 2026 20:38:20 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Smart Speakers]]></category>
                                                    <category><![CDATA[E-Readers]]></category>
                                                    <category><![CDATA[Routers]]></category>
                                                    <category><![CDATA[Streaming Devices]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Speakers]]></category>
                                                    <category><![CDATA[Computing]]></category>
                                                    <category><![CDATA[Tablets]]></category>
                                                    <category><![CDATA[Computing Hardware]]></category>
                                                    <category><![CDATA[Entertainment]]></category>
                                                    <category><![CDATA[Streaming]]></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.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;br&gt;Send secure tips on Signal: scyo1.55.&lt;/p&gt; ]]></dc:description>
                                                                                                        <dc:contributor><![CDATA[ Mike Prospero ]]></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/wqVGnqkQWWiAV4PhBUSR8-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Echo Dot with Clock review]]></media:description>                                                            <media:text><![CDATA[Echo Dot with Clock review]]></media:text>
                                <media:title type="plain"><![CDATA[Echo Dot with Clock review]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/wqVGnqkQWWiAV4PhBUSR8-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Over the weekend, Amazon quietly raised prices for nearly all of the devices the company makes, including the <a href="https://www.tomsguide.com/us/amazon-echo-dot-gen-3,review-5811.html">Echo Dot</a>, <a href="https://www.tomsguide.com/tablets/e-readers/kindle-paperwhite-2024-review">Kindle Paperwhite</a> and<a href="https://www.tomsguide.com/entertainment/streaming-devices/amazon-launches-updated-fire-tv-stick-hd-thats-slimmer-and-doesnt-require-a-wall-adapter"> Fire TV Stick HD</a>. It's the latest in a series of price increases that have hit nearly every company. You can guess what's to blame.</p><p>Amazon's new prices vary in amount, with at least one device seeing a 60% increase; the company cited the ongoing <a href="https://www.tomsguide.com/computing/the-ram-crisis-is-so-much-bigger-than-pcs-heres-the-rest-of-the-products-caught-in-the-crossfire">RAM crisis</a> and component inflation. First spotted by <a href="https://fortune.com/2026/08/21/exclusive-amazon-quietly-hiked-prices-echo-fire-tv-kindle-eero-significant-increases-memory-costs/" target="_blank">Fortune</a>, some of the company's cheaper devices appear to be the most affected. </p><p>Here's what the new prices look like:</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="FYUSTSLJG4LM47i68vjPdg" name="Echo Show 21-5" alt="Watching Fire TV on the Echo Show 21 in the kitchen" src="https://cdn.mos.cms.futurecdn.net/FYUSTSLJG4LM47i68vjPdg.jpg" mos="" align="middle" fullscreen="" width="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><div ><table><caption>Smart speakers and displays</caption><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>Original price</strong></p></th><th  ><p><strong>New price</strong></p></th><th  ><p><strong>Increase</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Echo Dot</strong></p></td><td  ><p>$49</p></td><td  ><p>$79</p></td><td  ><p>61.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Dot Max</strong></p></td><td  ><p>$99</p></td><td  ><p>$119</p></td><td  ><p>20.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Studio</strong></p></td><td  ><p>$219</p></td><td  ><p>$219</p></td><td  ><p>0%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 5</strong></p></td><td  ><p>$89</p></td><td  ><p>$99</p></td><td  ><p>11.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 8</strong></p></td><td  ><p>$179</p></td><td  ><p>$199</p></td><td  ><p>11.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 11</strong></p></td><td  ><p>$219</p></td><td  ><p>$249</p></td><td  ><p>13.7%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 15</strong></p></td><td  ><p>$299</p></td><td  ><p>$349</p></td><td  ><p>16.7%</p></td></tr><tr><td class="firstcol " ><p><strong>Echo Show 21</strong></p></td><td  ><p>$399</p></td><td  ><p>$499</p></td><td  ><p>25.1%</p></td></tr></tbody></table></div><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Bqve9zMXpkbhMu5Ex5nCpb" name="Amazon Fire TV stick 4K max" alt="Amazon Fire TV stick 4K max" src="https://cdn.mos.cms.futurecdn.net/Bqve9zMXpkbhMu5Ex5nCpb.jpg" mos="" align="middle" fullscreen="" width="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><div ><table><caption>Fire TV sticks</caption><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>Original Price</strong></p></th><th  ><p><strong>New Price</strong></p></th><th  ><p><strong>Increase</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Amazon Fire TV Stick HD</strong></p></td><td  ><p>$35</p></td><td  ><p>$39</p></td><td  ><p>11.4%</p></td></tr><tr><td class="firstcol " ><p><strong>Fire TV Stick 4K Select</strong></p></td><td  ><p>$39</p></td><td  ><p>$49</p></td><td  ><p>25.6%</p></td></tr><tr><td class="firstcol " ><p><strong>Fire TV Stick 4K Max</strong></p></td><td  ><p>$59</p></td><td  ><p>$84</p></td><td  ><p>42.4%</p></td></tr></tbody></table></div><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KVyvXAxLs7HEWJvCgxxEmM" name="Amazon Kindle Colorsoft-6.jpg" alt="Kindle Colorsoft" src="https://cdn.mos.cms.futurecdn.net/KVyvXAxLs7HEWJvCgxxEmM.jpg" mos="" align="middle" fullscreen="" width="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><div ><table><caption>Kindles</caption><thead><tr><th class="firstcol empty" ></th><th  ><p><strong>Original Price</strong></p></th><th  ><p><strong>New Price</strong></p></th><th  ><p><strong>Increase</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Kindle</strong></p></td><td  ><p>$110</p></td><td  ><p>$149</p></td><td  ><p>35.5%</p></td></tr><tr><td class="firstcol " ><p><strong>Kindle Paperwhite</strong></p></td><td  ><p>$159</p></td><td  ><p>$199</p></td><td  ><p>25.2%</p></td></tr><tr><td class="firstcol " ><p><strong>Kindle</strong> <strong>Colorsoft</strong></p></td><td  ><p>$249</p></td><td  ><p>$289</p></td><td  ><p>16.1%</p></td></tr></tbody></table></div><p>The Echo Dot saw the largest increase up 60% and $30, from $49 to $79, while the higher-tier Echo Show 11 is also getting a $30 hike from $219 to $249.</p><p>Additionally, the 16GB Kindle and Kindle Paperwhite saw hikes, going from $109 to $149 and $159 to $199, respectively. The Fire TV Stick HD only saw a $5 hike from $34 to $39 and the 4K Max rose from $59 to $84. </p><p>Surprisingly, the Amazon-owned <a href="https://www.tomsguide.com/face-off/ring-video-doorbell-3-vs-doorbell-pro">Ring lineup of doorbells and security cameras</a> didn't get more expensive. </p><p>An Amazon spokesperson told Tom's Guide, "After absorbing these increases for as long as we could, we recently adjusted pricing across our product lines." She noted that "significant" increases in memory and component costs led to the decision.</p><h2 id="still-a-cheaper-option">Still a cheaper option</h2><p>In some cases, despite the price hikes, Amazon's devices might still be the cheaper option. Unfortunately, that's because everything else also got more expensive this year. </p><p>In July, <a href="https://www.tomsguide.com/entertainment/streaming-devices/some-streaming-sticks-are-doubling-in-price-heres-whats-driving-up-costs">Roku bumped prices</a> on its streaming devices, with the Roku hitting $150 and the entry-level Roku Streaming Stick now at $40, up from $30.</p><p>Kindles might be more expensive than similar rival options like the Nook GlowLight 4, which comes with 32GB of memory for $149. </p><p>The Echo Dot, though, remains cheaper than the <a href="https://www.tomsguide.com/audio/smart-speakers/google-home-speaker-review">Google Home Speaker</a>, which starts at $99, though the gap is only $20 now. </p><h2 id="amazon-devices-outlook">Amazon Devices: 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="g4rv5xpNBNaytDaHXvb2nM" name="Echo Show 8 (4th Gen)--3" alt="The Echo Show 8 (4th Gen) with the Alexa+ logo on screen on a desk" src="https://cdn.mos.cms.futurecdn.net/g4rv5xpNBNaytDaHXvb2nM.jpg" mos="" align="middle" fullscreen="" width="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>At the moment, only the Fire tablets seem to be unaffected — in fact, they’re the only devices currently on sale, but this could be due largely to the fact that it’s <a href="https://www.tomsguide.com/news/ultimate-back-to-school-guide">back to school season</a>. </p><p>For years, Amazon’s Echo smart home speakers have been the company’s trojan horse to get its voice assistant in homes across the world. Given that <a href="https://www.tomsguide.com/ai/alexa-release-date-cost-devices-and-all-the-new-ai-features">Alexa+</a> is available on virtually every Amazon Echo device, the company may have calculated that it’s saturated the market enough so that adoption of Alexa+ wouldn’t be affected by any potential loss in sales.</p><p>Still, the timing is curious, as the company also announced last week that <a href="https://www.aboutamazon.com/news/devices/alexa-plus-fire-tv-free-ai">Alexa+ would be available, free of charge</a>, to its newest Fire TV devices. That means if you have a current-generation Amazon Fire TV Stick, the Fire TV Cube, or Amazon Ember smart TV, you can get the full version of Amazon’s AI-enabled assistant without needing a Prime account, or paying $19.99 per month. </p><p>Regardless of whether you want Alexa+, more than ever it’s worth waiting until Black Friday or Prime Day to purchase one of Amazon’s devices (sadly, the new sale price may be the old regular price). If you don’t want to wait that long, some third-party retailers are still offering the original price. For example, you can still find the Amazon Echo Dot for $49 at <a href="https://www.target.com/p/amazon-echo-dot-5th-gen-2022-smart-speaker-with-alexa-glacier-white/-/A-87130563" target="_blank" rel="nofollow">Target</a> and <a href="https://www.homedepot.com/pep/Amazon-Echo-Dot-5th-Gen-Smart-Speaker-with-Alexa-Deep-Sea-Blue-B09B93ZDG4/323260668" target="_blank" rel="nofollow">Home Depot</a>. </p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><strong></strong><a href="https://www.tomsguide.com/ai/my-12-year-old-noticed-a-missing-amazon-shopping-feature-so-i-tried-to-build-it-with-ai"><strong>My 12-year-old noticed a missing Amazon shopping feature — so I tried to build it with AI</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/sales-events/ive-had-amazon-prime-for-over-a-decade-heres-the-9-best-perks-youre-paying-for-but-probably-not-using"><strong>9 best Amazon Prime perks you're paying for but probably not using</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/sales-events/amazon-has-a-hidden-button-that-connects-you-with-a-real-customer-service-agent-heres-where-to-find-it"><strong>Amazon has a hidden button that connects you with a real customer service agent — here's where to find it</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I paired my iPhone with the Insta360 Mic Pro — and it beats my old DJI Mic with one neat feature ]]></title>
                                                                                                <dc:content><![CDATA[ <p>For years, the original <a href="https://www.amazon.com/DJI-Charging-Microphone-Cancellation-Interview/dp/B09GYD55JF/" target="_blank" rel="nofollow">DJI Mic</a> has been my undisputed daily driver for capturing crisp, reliable audio on the go. I’ve tested plenty of alternatives, but nothing truly managed to dethrone it — until now.</p><p>On paper, the new <a href="https://www.insta360.com/product/insta360-mic-pro" target="_blank" rel="nofollow">Insta360 Mic Pro</a> nails all the fundamentals I expect from a flagship wireless setup: seamless plug-and-play compatibility across smartphones and dedicated cameras, clean stereo recording, and onboard local backup storage so I never lose a take.</p><p>Yet, none of those standard specs are what actually won me over. The real standout is a feature I’ve never seen on a wireless microphone system before. While I initially wrote off the built-in e-ink display on each tiny transmitter as a novelty, it adds a layer of personalization in this age of content creation — where practically everyone is shooting video on the fly.</p><div class="product"><a data-dimension112="c056be84-9dce-11f1-bbd8-f75ce92bd6b8" data-action="Deal Block" data-label="Mic Pro" data-dimension48="Mic Pro" data-dimension25="$329.99" href="https://www.amazon.com/Insta360-Microphone-Recording-Bluetooth-Customizable/dp/B0GVSMTFH8/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:600px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="7AuF585NVdGEnNJAtzWPVW" name="Insta360 Mic Pro deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/7AuF585NVdGEnNJAtzWPVW.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Capture studio-grade sound anywhere with the Insta360 Mic Pro, a wireless microphone system that features two transmitters, receiver that works with phones and cameras, local recording, auto gain adjustment, and an innovative e-ink display on each transmitter that really adds a layer of personalization.<a class="view-deal button" href="https://www.amazon.com/Insta360-Microphone-Recording-Bluetooth-Customizable/dp/B0GVSMTFH8/" target="_blank" rel="nofollow" data-dimension112="c056be84-9dce-11f1-bbd8-f75ce92bd6b8" data-action="Deal Block" data-label="Mic Pro" data-dimension48="Mic Pro" data-dimension25="$329.99">View Deal</a></p></div><h2 id="it-s-all-about-the-branding">It’s all about the branding</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/9NMZULykoUsaYucqzntsXS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/kfVPuV7pFWxCfqEjePumWS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/yigLjFMbNZtKHTUsGRaBTS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>Most weekends, I spend time working with small businesses and non-profits to create, capture, and produce all sorts of video content. What makes the Insta360 Mic Pro special is how easily it personalizes a video, thanks to the neat e-ink display on both transmitters. It's the same reason why I use the <a href="https://www.tomsguide.com/phones/iphones/all-other-iphone-cases-are-dead-to-me-after-testing-this-one-with-an-e-ink-display-heres-why">Xtrem Inno</a> case with my <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-17-pro-review">iPhone 17 Pro</a>. </p><p>With its customizable, full-color e-ink display, I can personalize each transmitter with custom artwork, channel logos, or client branding in a matter of seconds right from my phone. I handed one to a client recently, and they were genuinely amused by the gesture — preferring it to having a generic black slab clipped to their shirt. </p><p>Best of all, swapping the image through the Insta360 app is also remarkably simple and intuitive. The app not only helps frame the image properly, but it also lets me adjust contrast and detail levels to get the best possible look. Once transferred, the graphic stays permanently on the screen until I change it — drawing virtually no power to remain visible.</p><p>For creators juggling multiple clients or trying to build their own recognizable presence, that quick visual touch instantly makes your production look more polished.</p><h2 id="record-confidently">Record confidently</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/2zVHv8cSrgFi8DhpUWsWTS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/459Fyo8iywWN235ykWeAWS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/5eVmSBE5FxVRT25GXpBRFS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9CnzTU4RbNzag4zqgXZEQS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mtK9MPaLjt6WZwMKSP3xyR.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>When you're out on fast-paced run-and-gun shoots, my iPhone 17 Pro makes it a breeze to quickly shoot clips — but the on-board mics can’t handle ambient sound levels that can spike without warning. That’s where the Insta360 Mic Pro’s auto gain control proves its worth.</p><p>During one of my shoots where I was recording a festival, the Insta360 Mic Pro dynamically adjusted the gain on the fly without the need of me doing it — so it perfectly handled a sequence when I was recording someone speaking when a band started playing in the background. With the DJI Mic, I’d have to turn down the gain level to compensate for the immense background noise disturbance.</p><p>Paired with its onboard noise cancellation — which does an impressive job muting distracting background noise without making vocals sound too artificial — it keeps dialogue clear and centered. Sure, there’s software that can do that in edit, but turning it on at the time of the shoot helps to save me time.</p><p>Add in local recording, which has already saved a few critical takes from unexpected wireless dropouts, and the entire system gives me the freedom to focus entirely on framing my shots rather than sweating over audio levels.</p><h2 id="highly-versatile">Highly versatile</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/nTGqghLNSb6U2m25jBbKSS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wFg43TdFfwqLkA6LxhvuPS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2E6VgqfJLwNQ6gjRv9wtPS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>One of the biggest strengths of the Insta360 Mic Pro is how effortlessly it bridges the gap between dedicated cameras and mobile rigs. Setup is so simple that when I connected the receiver to my <a href="https://www.tomsguide.com/phones/motorola-phones/motorola-razr-fold-review">Motorola Razr Fold</a>, the native camera app immediately supported external wireless audio without requiring third-party camera apps or tedious workarounds.</p><p>Beyond straightforward connectivity, the tactile controls on the transmitter itself make handling adjustments a breeze. With dedicated onboard buttons, I can trigger local backup recording in a pinch, mute the mic instantly, or cycle through three distinct voice tone presets to best highlight the speaker’s vocal profile. Topping it all off is the compact charging case, which neatly stores everything and ensures the entire kit is always powered and ready the moment I pull it out of my bag.</p><p>The standard Insta360 Mic Pro kit (two transmitters, one receiver, and the charging case) retails for $329.99, positioning it competitively alongside other flagship wireless systems. For solo creators, there's also a single transmitter and receiver option available for $199.99.</p><p>Best of all, scaling up to multiple mics really lets that e-ink display shine. It’s a unique angle I hadn’t even considered in all my years of testing gear: by integrating an e-ink screen, it truly transforms what used to be generic hardware into a customizable piece of branding that sets your videos apart.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-WQwxzX"></div>                            </div>                            <script src="https://kwizly.com/embed/WQwxzX.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><strong></strong><a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-everything-we-know-so-far-about-apples-camera-earbuds"><strong>AirPods Ultra: Everything we know so far about Apple's camera earbuds</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/phones/iphones/ios-27s-safety-filter-was-ruining-my-facetime-calls-heres-how-i-turned-it-off"><strong>iOS 27’s safety filter was ruining my FaceTime calls — here’s how I turned it off</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/tvs/best-buys-60th-anniversary-sale-is-live-here-are-my-top-9-tv-deals-on-qleds-and-oleds-starting-from-usd279"><strong>Best Buy's 60th Anniversary Sale is live — here are my top 9 TV deals on QLEDs and OLEDs starting from $279</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/phones/i-paired-my-iphone-with-the-insta360-mic-pro-and-it-beats-my-old-dji-mic-with-one-neat-feature</link>
                                                                            <description>
                            <![CDATA[ The Insta360 Mic Pro delivers reliable audio performance and seamless connectivity with a unique twist: a customizable e-ink display. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">HNuff7jHZPS7CUPfQX8pzY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/yigLjFMbNZtKHTUsGRaBTS-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 24 Aug 2026 06:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Phones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                                                                <author><![CDATA[ john.velasco@futurenet.com (John Velasco) ]]></author>                    <dc:creator><![CDATA[ John Velasco ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/TSj224oX8d5ERXaDs8pDGd.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/yigLjFMbNZtKHTUsGRaBTS-1280-80.jpg">
                                                            <media:credit><![CDATA[John Velasco / Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Insta360 Mic Pro used with iPhone.]]></media:description>                                                            <media:text><![CDATA[Insta360 Mic Pro used with iPhone.]]></media:text>
                                <media:title type="plain"><![CDATA[Insta360 Mic Pro used with iPhone.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/yigLjFMbNZtKHTUsGRaBTS-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>For years, the original <a href="https://www.amazon.com/DJI-Charging-Microphone-Cancellation-Interview/dp/B09GYD55JF/" target="_blank" rel="nofollow">DJI Mic</a> has been my undisputed daily driver for capturing crisp, reliable audio on the go. I’ve tested plenty of alternatives, but nothing truly managed to dethrone it — until now.</p><p>On paper, the new <a href="https://www.insta360.com/product/insta360-mic-pro" target="_blank" rel="nofollow">Insta360 Mic Pro</a> nails all the fundamentals I expect from a flagship wireless setup: seamless plug-and-play compatibility across smartphones and dedicated cameras, clean stereo recording, and onboard local backup storage so I never lose a take.</p><p>Yet, none of those standard specs are what actually won me over. The real standout is a feature I’ve never seen on a wireless microphone system before. While I initially wrote off the built-in e-ink display on each tiny transmitter as a novelty, it adds a layer of personalization in this age of content creation — where practically everyone is shooting video on the fly.</p><div class="product"><a data-dimension112="c056be84-9dce-11f1-bbd8-f75ce92bd6b8" data-action="Deal Block" data-label="Mic Pro" data-dimension48="Mic Pro" data-dimension25="$329.99" href="https://www.amazon.com/Insta360-Microphone-Recording-Bluetooth-Customizable/dp/B0GVSMTFH8/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:600px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="7AuF585NVdGEnNJAtzWPVW" name="Insta360 Mic Pro deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/7AuF585NVdGEnNJAtzWPVW.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Capture studio-grade sound anywhere with the Insta360 Mic Pro, a wireless microphone system that features two transmitters, receiver that works with phones and cameras, local recording, auto gain adjustment, and an innovative e-ink display on each transmitter that really adds a layer of personalization.<a class="view-deal button" href="https://www.amazon.com/Insta360-Microphone-Recording-Bluetooth-Customizable/dp/B0GVSMTFH8/" target="_blank" rel="nofollow" data-dimension112="c056be84-9dce-11f1-bbd8-f75ce92bd6b8" data-action="Deal Block" data-label="Mic Pro" data-dimension48="Mic Pro" data-dimension25="$329.99">View Deal</a></p></div><h2 id="it-s-all-about-the-branding">It’s all about the branding</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/9NMZULykoUsaYucqzntsXS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/kfVPuV7pFWxCfqEjePumWS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/yigLjFMbNZtKHTUsGRaBTS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>Most weekends, I spend time working with small businesses and non-profits to create, capture, and produce all sorts of video content. What makes the Insta360 Mic Pro special is how easily it personalizes a video, thanks to the neat e-ink display on both transmitters. It's the same reason why I use the <a href="https://www.tomsguide.com/phones/iphones/all-other-iphone-cases-are-dead-to-me-after-testing-this-one-with-an-e-ink-display-heres-why">Xtrem Inno</a> case with my <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-17-pro-review">iPhone 17 Pro</a>. </p><p>With its customizable, full-color e-ink display, I can personalize each transmitter with custom artwork, channel logos, or client branding in a matter of seconds right from my phone. I handed one to a client recently, and they were genuinely amused by the gesture — preferring it to having a generic black slab clipped to their shirt. </p><p>Best of all, swapping the image through the Insta360 app is also remarkably simple and intuitive. The app not only helps frame the image properly, but it also lets me adjust contrast and detail levels to get the best possible look. Once transferred, the graphic stays permanently on the screen until I change it — drawing virtually no power to remain visible.</p><p>For creators juggling multiple clients or trying to build their own recognizable presence, that quick visual touch instantly makes your production look more polished.</p><h2 id="record-confidently">Record confidently</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/2zVHv8cSrgFi8DhpUWsWTS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/459Fyo8iywWN235ykWeAWS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/5eVmSBE5FxVRT25GXpBRFS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9CnzTU4RbNzag4zqgXZEQS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mtK9MPaLjt6WZwMKSP3xyR.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>When you're out on fast-paced run-and-gun shoots, my iPhone 17 Pro makes it a breeze to quickly shoot clips — but the on-board mics can’t handle ambient sound levels that can spike without warning. That’s where the Insta360 Mic Pro’s auto gain control proves its worth.</p><p>During one of my shoots where I was recording a festival, the Insta360 Mic Pro dynamically adjusted the gain on the fly without the need of me doing it — so it perfectly handled a sequence when I was recording someone speaking when a band started playing in the background. With the DJI Mic, I’d have to turn down the gain level to compensate for the immense background noise disturbance.</p><p>Paired with its onboard noise cancellation — which does an impressive job muting distracting background noise without making vocals sound too artificial — it keeps dialogue clear and centered. Sure, there’s software that can do that in edit, but turning it on at the time of the shoot helps to save me time.</p><p>Add in local recording, which has already saved a few critical takes from unexpected wireless dropouts, and the entire system gives me the freedom to focus entirely on framing my shots rather than sweating over audio levels.</p><h2 id="highly-versatile">Highly versatile</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/nTGqghLNSb6U2m25jBbKSS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wFg43TdFfwqLkA6LxhvuPS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2E6VgqfJLwNQ6gjRv9wtPS.jpg" alt="Insta360 Mic Pro used with iPhone." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>One of the biggest strengths of the Insta360 Mic Pro is how effortlessly it bridges the gap between dedicated cameras and mobile rigs. Setup is so simple that when I connected the receiver to my <a href="https://www.tomsguide.com/phones/motorola-phones/motorola-razr-fold-review">Motorola Razr Fold</a>, the native camera app immediately supported external wireless audio without requiring third-party camera apps or tedious workarounds.</p><p>Beyond straightforward connectivity, the tactile controls on the transmitter itself make handling adjustments a breeze. With dedicated onboard buttons, I can trigger local backup recording in a pinch, mute the mic instantly, or cycle through three distinct voice tone presets to best highlight the speaker’s vocal profile. Topping it all off is the compact charging case, which neatly stores everything and ensures the entire kit is always powered and ready the moment I pull it out of my bag.</p><p>The standard Insta360 Mic Pro kit (two transmitters, one receiver, and the charging case) retails for $329.99, positioning it competitively alongside other flagship wireless systems. For solo creators, there's also a single transmitter and receiver option available for $199.99.</p><p>Best of all, scaling up to multiple mics really lets that e-ink display shine. It’s a unique angle I hadn’t even considered in all my years of testing gear: by integrating an e-ink screen, it truly transforms what used to be generic hardware into a customizable piece of branding that sets your videos apart.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-WQwxzX"></div>                            </div>                            <script src="https://kwizly.com/embed/WQwxzX.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><strong></strong><a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-everything-we-know-so-far-about-apples-camera-earbuds"><strong>AirPods Ultra: Everything we know so far about Apple's camera earbuds</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/phones/iphones/ios-27s-safety-filter-was-ruining-my-facetime-calls-heres-how-i-turned-it-off"><strong>iOS 27’s safety filter was ruining my FaceTime calls — here’s how I turned it off</strong></a><strong></strong></li><li><strong></strong><a href="https://www.tomsguide.com/tvs/best-buys-60th-anniversary-sale-is-live-here-are-my-top-9-tv-deals-on-qleds-and-oleds-starting-from-usd279"><strong>Best Buy's 60th Anniversary Sale is live — here are my top 9 TV deals on QLEDs and OLEDs starting from $279</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ This $800 record player might be the best turntable under $1000 ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">Fluance RT87 review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$799/£630/$1,200$AU<br><strong>Dimensions: </strong>6.3 x 16.5 x 14.25 inches<br><strong>Weight: </strong>21 pounds<br><strong>Color: </strong>Natural walnut, Piano Black, White<br><strong>Type: </strong>Dual Plinth reference turntable<br><strong>Speeds: </strong>33 ⅓, 45, 78RPM<br><strong>Controls: </strong>Power, speed control<br><strong>Connectivity: </strong>RCA (no phono preamp)<br><strong>Stylus: </strong>2M Blue, AT-VM95ML</p></div></div><p>The world of high-end turntables is a tough nut to crack. There are exotic materials to consider, from metal coverings to interiors lined with special resins. Some float to get rid of vibrations; some are made out of the same stuff as hypercars. </p><p>All of them, though, cost thousands of dollars. High-end sound has always been the territory of the ultra-wealthy — until now. The Fluance RT87 brings excellent sound to the masses, with snazzy construction and some premium features for a price that undercuts just about everyone else.</p><p>But is it one of the <a href="https://www.tomsguide.com/best-picks/best-record-players"><u>best record players</u></a>? Should you spend your hard-earned $800 on a turntable that needs extra stuff to make it work? Find out in my Fluance RT87 review.</p><h3 class="article-body__section" id="section-fluance-rt87-review-price-and-availability"><span>Fluance RT87 review: Price and availability</span></h3><p>On first impression, the RT87 seems like an expensive turntable. There’s no built-in pre-amp, so you’ll need to buy one separately. It’s entirely manual, apart from electronic speed control. The anti-skate weight is just that — a weight. For some, this could make the turntable’s $799 price tag seem steep.</p><div><blockquote><p>The RT87 makes a great case for its loftier price — and then you pull it from its cardboard prison, assemble it, plug it in, and drop the needle. Suddenly, $799 starts to feel like something of a bargain.</p></blockquote></div><p>But then you start to add up what comes in the box: The electronic speed control and silent motor start to become a boon, and the manual nature makes sense when you see the carbon tone arm. There’s a premium cartridge from either Ortofon or Audio-Technica to read those all-important record grooves. The dual plinth reduces vibrations.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jeAMwFcGZ3gQMzEyTG3xJZ" name="Fluance RT87 turntable-2" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/jeAMwFcGZ3gQMzEyTG3xJZ.jpg" mos="" align="middle" fullscreen="" width="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>Comparison to other, similarly specced turntables starts to make the price of the RT87 sting a whole lot less. My favorite turntable, the <a href="https://www.tomsguide.com/audio/rega-planar-3-rs-review"><u>Rega Planar 3 RS</u></a>, costs thousands of dollars. The <a href="https://www.tomsguide.com/audio/i-tested-this-stunning-turntable-with-top-notch-sound-thats-great-for-beginners-theres-just-one-big-problem"><u>Goldring GR3</u></a>, the last TT I tested, costs about the same with fewer premium features.</p><p>For a reference turntable, the RT87 makes a great case for its loftier price — and then you pull it from its cardboard prison, assemble it, plug it in, and drop the needle. Suddenly, $799 starts to feel like something of a bargain.</p><h3 class="article-body__section" id="section-fluance-rt87-review-design"><span>Fluance RT87 review: Design</span></h3><p>I love the way that the RT87 looks. The dual plinth design makes it look unlike any other sub-$1000 turntable and lends a commanding presence to the deck. The carbon tonearm is a nice visual touch, and the hefty acrylic platter really brings everything together. This is a turntable that looks like it means business from foot to dustcover.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9MUUdusSjjnLnMNrSajDKZ" name="Fluance RT87 turntable-4" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/9MUUdusSjjnLnMNrSajDKZ.jpg" mos="" align="middle" fullscreen="" width="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>Then you start to notice the little details. The screw holes are filled with rubber stoppers so that you can’t see them through the frosted platter. The metal spindle and metallic surround in the center reduce vibration and motor judder. There are as few controls as possible for a clean look; the only thing on show is the speed control, which doubles as a power switch.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="mzgHzxjYpaYqD6JgM4GbKZ" name="Fluance RT87 turntable-7" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/mzgHzxjYpaYqD6JgM4GbKZ.jpg" mos="" align="middle" fullscreen="" width="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>Then it's all wrapped in that lovely gloss finish. I reviewed the natural walnut option, and it looks lovely. It makes the whole thing feel very polished and premium — and like it costs a whole lot more than its price would suggest.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hWKEguJEKSrPs432W5mUMZ" name="Fluance RT87 turntable-15" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/hWKEguJEKSrPs432W5mUMZ.jpg" mos="" align="middle" fullscreen="" width="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 some slight hiccups in places, however. The counterweight sits on a hollow tube that feels a little cheap, although at least it's metal. The anti-skate weight has a habit of falling off while you move the tonearm around, but never enough to be too annoying. The dust cover hinges are slightly looser than some I’ve used, but I’ve never had the cover drop during play.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="EKrj8r5u9MVnU6NSwoqrLZ" name="Fluance RT87 turntable-14" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/EKrj8r5u9MVnU6NSwoqrLZ.jpg" mos="" align="middle" fullscreen="" width="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>Overall, this is a well-designed, premium-looking turntable that’ll look great atop your HiFi rack. Just be wary that it's pretty heavy at 21lbs.</p><h3 class="article-body__section" id="section-fluance-rt87-review-setting-up"><span>Fluance RT87 review: Setting up</span></h3><p>There’s some work you’ll need to do when you pull the turntable from the cellophane and polystyrene in which it comes delivered. The tonearm needs balancing. This is easy enough if you’ve balanced a tonearm before, but you might need to watch a YouTube tutorial if this is your first time.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="cvVE4oHHThsmbj9d78hBPZ" name="Fluance RT87 turntable-10" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/cvVE4oHHThsmbj9d78hBPZ.jpg" mos="" align="middle" fullscreen="" width="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>First and perhaps most important is to remove the transport screws — they’re long and designed to hold the dual plinth in place during delivery. Keep hold of them should you need to move the turntable again in the future. Then you can fill the holes with the little rubber stoppers.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2N7busz3PVRrMcE6NqRrKZ" name="Fluance RT87 turntable-16" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/2N7busz3PVRrMcE6NqRrKZ.jpg" mos="" align="middle" fullscreen="" width="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>Pop the platter onto the spindle, and then run the drive belt around the edge of the platter and the motor pulley. It’s a pain, but it doesn’t come off during use, so it’s a “one and done” kind of 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XZLxFHEXgcojgaa4nnVyHZ" name="Fluance RT87 turntable-8" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/XZLxFHEXgcojgaa4nnVyHZ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You’ll also be fitting the cartridge to the end of the tone arm — easy enough. Finally, you’ll fit the dust cover. I found fitting the hinges into the dust cover first, then onto the plinth worked well. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="guC9HRYeMwoZNoBZ2jdFLZ" name="Fluance RT87 turntable-12" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/guC9HRYeMwoZNoBZ2jdFLZ.jpg" mos="" align="middle" fullscreen="" width="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 looking for something with minimal setup, this might not be the turntable for you, but it’s rare to find a record player of this caliber with a less-involved setup process. This is about standard in turntable land.</p><h3 class="article-body__section" id="section-fluance-rt87-review-features"><span>Fluance RT87 review: Features</span></h3><p>If you’re looking for fancy tech and super-futuristic features, there are other decks out there that might be more your style. All the extra features here are to make the turntable sound as good as possible without touching a circuit board, from structure and construction to clever design touches.</p><div><blockquote><p>I could dance and headbang to my favorite tunes with nary a twitch from the stylus. It stays rock solid in the groove, every jump and air guitar solo absorbed by the plinth design.</p></blockquote></div><p>First and foremost is that dual plinth design. It looks cool, with two plinth layers that almost hover apart. But what does it do? It’s all about reducing vibration as much as possible. To do that, the vibration-dampening feet absorb as much vibration as possible. Any that make it through are dampened by the first plinth, which is isolated from the second. That means that the second plinth shouldn’t suffer from any unwanted vibrations.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="DbeVZ9rGDxhUaunkTsd5NZ" name="Fluance RT87 turntable-5" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/DbeVZ9rGDxhUaunkTsd5NZ.jpg" mos="" align="middle" fullscreen="" width="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>Does it work? It does, actually. Rather well. I could dance and headbang to my favorite tunes with nary a twitch from the stylus. It stays rock solid in the groove, every jump and air guitar solo absorbed by the plinth design. Very clever and very impressive.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ySFpBSXQ4BGXtnFnfEtmNZ" name="Fluance RT87 turntable-9" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/ySFpBSXQ4BGXtnFnfEtmNZ.jpg" mos="" align="middle" fullscreen="" width="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 carbon tonearm is all about negating vibration while remaining as stiff as possible while reducing the overall weight. I would have liked a more impressive anti-skate than the typical weight on some fishing line we’ve got here, but it's more anti-skate than we get on some very premium models.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NBT4tVDdcubFU2FnLXdtMZ" name="Fluance RT87 turntable-18" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/NBT4tVDdcubFU2FnLXdtMZ.jpg" mos="" align="middle" fullscreen="" width="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>Other than that, this is a very simple turntable. On the back there’s a pair of RCA connectors, a grounding post, the power port, and a switch to turn the auto-stop on and off. That’s about as high tech as we get here, folks — it stops the record when the stylus reaches the run-out groove. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="b9mJPdPKfCtotNBAVk5SPZ" name="Fluance RT87 turntable-17" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/b9mJPdPKfCtotNBAVk5SPZ.jpg" mos="" align="middle" fullscreen="" width="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>But a little expert advice here — leave it turned off. It’s not that it doesn’t work, but because no mechanism lifts the tonearm, your stylus will end up stuck in the run-out groove, unmoving. Don’t do that. Just watch the record as it's coming to the end of play, and move the stylus once the record is over. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3brrJFNqkoRxXXqHRvtWJZ" name="Fluance RT87 turntable-6" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/3brrJFNqkoRxXXqHRvtWJZ.jpg" mos="" align="middle" fullscreen="" width="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>Oh, one last thing — 78RPM fans, rejoice! You can play your 78s here. Just remember that you’ll need to change out the cartridge to something compatible. Those wider grooves will chew up your standard stylus.</p><h3 class="article-body__section" id="section-fluance-rt87-review-sound-quality"><span>Fluance RT87 review: Sound quality</span></h3><p>If you’re after the warmest-sounding turntable around, you should probably look elsewhere, or make sure you pair this one with a suitable amp or pre-amp. This is a lively-sounding deck despite all that extra wood, and detail retrieval shines no matter the genre of vinyl that spins on the platter.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="FjYiXpER4k6pwDTC9z94JZ" name="Fluance RT87 turntable-11" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/FjYiXpER4k6pwDTC9z94JZ.jpg" mos="" align="middle" fullscreen="" width="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>Don’t get me wrong, there’s some nice low-end to enjoy here — but it's not in your face or particularly dominating. Instead, the RT87 goes for a much bigger, wider sound. This turntable sounds massive no matter the system you play it 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="Kwy6Avm36HRiw4gx2HMYKZ" name="Fluance RT87 turntable-13" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/Kwy6Avm36HRiw4gx2HMYKZ.jpg" mos="" align="middle" fullscreen="" width="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 my records through an iFi pre-amp going into a Cambridge Audio EXA100. That goes into a pair of Focal Vestia No. 3 floorstanders — a system that <em>needs </em>powerful sound and reveals bad audio. The RT87 had no problem.</p><p>The sawblade guitars of Bloodbath’s “Survival of the Sickest” cut through like a jagged knife, filling my office room with suitable menace and violence. Dua Lipa’s vocals were a highlight during my testing, revealing some wonderful clarity.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jhThoaCSyAByscMmyYQsLZ" name="Fluance RT87 turntable-3" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/jhThoaCSyAByscMmyYQsLZ.jpg" mos="" align="middle" fullscreen="" width="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>More expensive models to the tune of thousands will sound better, with more robust low-end and perhaps a little more sonic bite. But you’d be hard-pressed to find something that sounds better within $300-$500 of the RT-87’s price. And that’s mighty impressive.</p><h3 class="article-body__section" id="section-verdict"><span>Verdict</span></h3><p>While the RT87 is expensive, it offers excellent value. Some more costly models envy the price-to-quality ratio here, and it’s mostly down to that very snazzy dual platter design.</p><p>Are there better-sounding turntables? Absolutely — but you’ll be paying hundreds if not thousands of dollars more to get the increase in performance from the likes of the Planar 3 RS, or even the Technics SL13000G. Great turntables that come for a hefty premium</p><p>This is a five-star deck through and through. My few nitpicks are few and far between, and don’t dull the luster of what is otherwise a spectacular buy for $800 or so. Just remember that you’ll need to factor in a phono pre-amp (and maybe some powered speakers if you don’t own some already), and you’re home free.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/fluance-rt87-review</link>
                                                                            <description>
                            <![CDATA[ Fluance’s latest addition to its lineup brings a premium look and feel while crucially keeping the price below $1,000. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ww5d2VeTsGk8X2XUJU3RxY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/3FaaNjzcUD4bEyXzHPi6KZ-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 20 Aug 2026 15:07:48 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Tammy Rogers ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/n8MswK9ZvXC9mSb5incU44.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Tammy&#039;s reviews and buying guide expertise have more focus than ever on Tom&#039;s Guide, helping buyers find the audio gear that works best for them. Tammy has worked with some of the most desirable audio brands on the planet in her time writing about headphones, speakers, and more, bringing a consumer focussed approach to critique and buying advice. Away from her desk, you&#039;ll probably find her in the countryside writing (extremely bad) poetry, or putting her screenwriting Masters to good use creating screenplays that&#039;ll never see the light of day.&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/3FaaNjzcUD4bEyXzHPi6KZ-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Fluance RT87 turntable on a white table]]></media:description>                                                            <media:text><![CDATA[Fluance RT87 turntable on a white table]]></media:text>
                                <media:title type="plain"><![CDATA[Fluance RT87 turntable on a white table]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/3FaaNjzcUD4bEyXzHPi6KZ-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">Fluance RT87 review: Specs</div><div class="fancy_box_body"><p class="fancy-box__body-text"><strong>Price: </strong>$799/£630/$1,200$AU<br><strong>Dimensions: </strong>6.3 x 16.5 x 14.25 inches<br><strong>Weight: </strong>21 pounds<br><strong>Color: </strong>Natural walnut, Piano Black, White<br><strong>Type: </strong>Dual Plinth reference turntable<br><strong>Speeds: </strong>33 ⅓, 45, 78RPM<br><strong>Controls: </strong>Power, speed control<br><strong>Connectivity: </strong>RCA (no phono preamp)<br><strong>Stylus: </strong>2M Blue, AT-VM95ML</p></div></div><p>The world of high-end turntables is a tough nut to crack. There are exotic materials to consider, from metal coverings to interiors lined with special resins. Some float to get rid of vibrations; some are made out of the same stuff as hypercars. </p><p>All of them, though, cost thousands of dollars. High-end sound has always been the territory of the ultra-wealthy — until now. The Fluance RT87 brings excellent sound to the masses, with snazzy construction and some premium features for a price that undercuts just about everyone else.</p><p>But is it one of the <a href="https://www.tomsguide.com/best-picks/best-record-players"><u>best record players</u></a>? Should you spend your hard-earned $800 on a turntable that needs extra stuff to make it work? Find out in my Fluance RT87 review.</p><h3 class="article-body__section" id="section-fluance-rt87-review-price-and-availability"><span>Fluance RT87 review: Price and availability</span></h3><p>On first impression, the RT87 seems like an expensive turntable. There’s no built-in pre-amp, so you’ll need to buy one separately. It’s entirely manual, apart from electronic speed control. The anti-skate weight is just that — a weight. For some, this could make the turntable’s $799 price tag seem steep.</p><div><blockquote><p>The RT87 makes a great case for its loftier price — and then you pull it from its cardboard prison, assemble it, plug it in, and drop the needle. Suddenly, $799 starts to feel like something of a bargain.</p></blockquote></div><p>But then you start to add up what comes in the box: The electronic speed control and silent motor start to become a boon, and the manual nature makes sense when you see the carbon tone arm. There’s a premium cartridge from either Ortofon or Audio-Technica to read those all-important record grooves. The dual plinth reduces vibrations.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jeAMwFcGZ3gQMzEyTG3xJZ" name="Fluance RT87 turntable-2" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/jeAMwFcGZ3gQMzEyTG3xJZ.jpg" mos="" align="middle" fullscreen="" width="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>Comparison to other, similarly specced turntables starts to make the price of the RT87 sting a whole lot less. My favorite turntable, the <a href="https://www.tomsguide.com/audio/rega-planar-3-rs-review"><u>Rega Planar 3 RS</u></a>, costs thousands of dollars. The <a href="https://www.tomsguide.com/audio/i-tested-this-stunning-turntable-with-top-notch-sound-thats-great-for-beginners-theres-just-one-big-problem"><u>Goldring GR3</u></a>, the last TT I tested, costs about the same with fewer premium features.</p><p>For a reference turntable, the RT87 makes a great case for its loftier price — and then you pull it from its cardboard prison, assemble it, plug it in, and drop the needle. Suddenly, $799 starts to feel like something of a bargain.</p><h3 class="article-body__section" id="section-fluance-rt87-review-design"><span>Fluance RT87 review: Design</span></h3><p>I love the way that the RT87 looks. The dual plinth design makes it look unlike any other sub-$1000 turntable and lends a commanding presence to the deck. The carbon tonearm is a nice visual touch, and the hefty acrylic platter really brings everything together. This is a turntable that looks like it means business from foot to dustcover.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9MUUdusSjjnLnMNrSajDKZ" name="Fluance RT87 turntable-4" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/9MUUdusSjjnLnMNrSajDKZ.jpg" mos="" align="middle" fullscreen="" width="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>Then you start to notice the little details. The screw holes are filled with rubber stoppers so that you can’t see them through the frosted platter. The metal spindle and metallic surround in the center reduce vibration and motor judder. There are as few controls as possible for a clean look; the only thing on show is the speed control, which doubles as a power switch.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="mzgHzxjYpaYqD6JgM4GbKZ" name="Fluance RT87 turntable-7" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/mzgHzxjYpaYqD6JgM4GbKZ.jpg" mos="" align="middle" fullscreen="" width="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>Then it's all wrapped in that lovely gloss finish. I reviewed the natural walnut option, and it looks lovely. It makes the whole thing feel very polished and premium — and like it costs a whole lot more than its price would suggest.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hWKEguJEKSrPs432W5mUMZ" name="Fluance RT87 turntable-15" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/hWKEguJEKSrPs432W5mUMZ.jpg" mos="" align="middle" fullscreen="" width="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 some slight hiccups in places, however. The counterweight sits on a hollow tube that feels a little cheap, although at least it's metal. The anti-skate weight has a habit of falling off while you move the tonearm around, but never enough to be too annoying. The dust cover hinges are slightly looser than some I’ve used, but I’ve never had the cover drop during play.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="EKrj8r5u9MVnU6NSwoqrLZ" name="Fluance RT87 turntable-14" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/EKrj8r5u9MVnU6NSwoqrLZ.jpg" mos="" align="middle" fullscreen="" width="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>Overall, this is a well-designed, premium-looking turntable that’ll look great atop your HiFi rack. Just be wary that it's pretty heavy at 21lbs.</p><h3 class="article-body__section" id="section-fluance-rt87-review-setting-up"><span>Fluance RT87 review: Setting up</span></h3><p>There’s some work you’ll need to do when you pull the turntable from the cellophane and polystyrene in which it comes delivered. The tonearm needs balancing. This is easy enough if you’ve balanced a tonearm before, but you might need to watch a YouTube tutorial if this is your first time.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="cvVE4oHHThsmbj9d78hBPZ" name="Fluance RT87 turntable-10" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/cvVE4oHHThsmbj9d78hBPZ.jpg" mos="" align="middle" fullscreen="" width="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>First and perhaps most important is to remove the transport screws — they’re long and designed to hold the dual plinth in place during delivery. Keep hold of them should you need to move the turntable again in the future. Then you can fill the holes with the little rubber stoppers.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2N7busz3PVRrMcE6NqRrKZ" name="Fluance RT87 turntable-16" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/2N7busz3PVRrMcE6NqRrKZ.jpg" mos="" align="middle" fullscreen="" width="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>Pop the platter onto the spindle, and then run the drive belt around the edge of the platter and the motor pulley. It’s a pain, but it doesn’t come off during use, so it’s a “one and done” kind of 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XZLxFHEXgcojgaa4nnVyHZ" name="Fluance RT87 turntable-8" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/XZLxFHEXgcojgaa4nnVyHZ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You’ll also be fitting the cartridge to the end of the tone arm — easy enough. Finally, you’ll fit the dust cover. I found fitting the hinges into the dust cover first, then onto the plinth worked well. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="guC9HRYeMwoZNoBZ2jdFLZ" name="Fluance RT87 turntable-12" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/guC9HRYeMwoZNoBZ2jdFLZ.jpg" mos="" align="middle" fullscreen="" width="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 looking for something with minimal setup, this might not be the turntable for you, but it’s rare to find a record player of this caliber with a less-involved setup process. This is about standard in turntable land.</p><h3 class="article-body__section" id="section-fluance-rt87-review-features"><span>Fluance RT87 review: Features</span></h3><p>If you’re looking for fancy tech and super-futuristic features, there are other decks out there that might be more your style. All the extra features here are to make the turntable sound as good as possible without touching a circuit board, from structure and construction to clever design touches.</p><div><blockquote><p>I could dance and headbang to my favorite tunes with nary a twitch from the stylus. It stays rock solid in the groove, every jump and air guitar solo absorbed by the plinth design.</p></blockquote></div><p>First and foremost is that dual plinth design. It looks cool, with two plinth layers that almost hover apart. But what does it do? It’s all about reducing vibration as much as possible. To do that, the vibration-dampening feet absorb as much vibration as possible. Any that make it through are dampened by the first plinth, which is isolated from the second. That means that the second plinth shouldn’t suffer from any unwanted vibrations.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="DbeVZ9rGDxhUaunkTsd5NZ" name="Fluance RT87 turntable-5" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/DbeVZ9rGDxhUaunkTsd5NZ.jpg" mos="" align="middle" fullscreen="" width="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>Does it work? It does, actually. Rather well. I could dance and headbang to my favorite tunes with nary a twitch from the stylus. It stays rock solid in the groove, every jump and air guitar solo absorbed by the plinth design. Very clever and very impressive.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ySFpBSXQ4BGXtnFnfEtmNZ" name="Fluance RT87 turntable-9" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/ySFpBSXQ4BGXtnFnfEtmNZ.jpg" mos="" align="middle" fullscreen="" width="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 carbon tonearm is all about negating vibration while remaining as stiff as possible while reducing the overall weight. I would have liked a more impressive anti-skate than the typical weight on some fishing line we’ve got here, but it's more anti-skate than we get on some very premium models.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NBT4tVDdcubFU2FnLXdtMZ" name="Fluance RT87 turntable-18" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/NBT4tVDdcubFU2FnLXdtMZ.jpg" mos="" align="middle" fullscreen="" width="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>Other than that, this is a very simple turntable. On the back there’s a pair of RCA connectors, a grounding post, the power port, and a switch to turn the auto-stop on and off. That’s about as high tech as we get here, folks — it stops the record when the stylus reaches the run-out groove. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="b9mJPdPKfCtotNBAVk5SPZ" name="Fluance RT87 turntable-17" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/b9mJPdPKfCtotNBAVk5SPZ.jpg" mos="" align="middle" fullscreen="" width="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>But a little expert advice here — leave it turned off. It’s not that it doesn’t work, but because no mechanism lifts the tonearm, your stylus will end up stuck in the run-out groove, unmoving. Don’t do that. Just watch the record as it's coming to the end of play, and move the stylus once the record is over. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3brrJFNqkoRxXXqHRvtWJZ" name="Fluance RT87 turntable-6" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/3brrJFNqkoRxXXqHRvtWJZ.jpg" mos="" align="middle" fullscreen="" width="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>Oh, one last thing — 78RPM fans, rejoice! You can play your 78s here. Just remember that you’ll need to change out the cartridge to something compatible. Those wider grooves will chew up your standard stylus.</p><h3 class="article-body__section" id="section-fluance-rt87-review-sound-quality"><span>Fluance RT87 review: Sound quality</span></h3><p>If you’re after the warmest-sounding turntable around, you should probably look elsewhere, or make sure you pair this one with a suitable amp or pre-amp. This is a lively-sounding deck despite all that extra wood, and detail retrieval shines no matter the genre of vinyl that spins on the platter.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="FjYiXpER4k6pwDTC9z94JZ" name="Fluance RT87 turntable-11" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/FjYiXpER4k6pwDTC9z94JZ.jpg" mos="" align="middle" fullscreen="" width="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>Don’t get me wrong, there’s some nice low-end to enjoy here — but it's not in your face or particularly dominating. Instead, the RT87 goes for a much bigger, wider sound. This turntable sounds massive no matter the system you play it 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="Kwy6Avm36HRiw4gx2HMYKZ" name="Fluance RT87 turntable-13" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/Kwy6Avm36HRiw4gx2HMYKZ.jpg" mos="" align="middle" fullscreen="" width="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 my records through an iFi pre-amp going into a Cambridge Audio EXA100. That goes into a pair of Focal Vestia No. 3 floorstanders — a system that <em>needs </em>powerful sound and reveals bad audio. The RT87 had no problem.</p><p>The sawblade guitars of Bloodbath’s “Survival of the Sickest” cut through like a jagged knife, filling my office room with suitable menace and violence. Dua Lipa’s vocals were a highlight during my testing, revealing some wonderful clarity.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jhThoaCSyAByscMmyYQsLZ" name="Fluance RT87 turntable-3" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/jhThoaCSyAByscMmyYQsLZ.jpg" mos="" align="middle" fullscreen="" width="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>More expensive models to the tune of thousands will sound better, with more robust low-end and perhaps a little more sonic bite. But you’d be hard-pressed to find something that sounds better within $300-$500 of the RT-87’s price. And that’s mighty impressive.</p><h3 class="article-body__section" id="section-verdict"><span>Verdict</span></h3><p>While the RT87 is expensive, it offers excellent value. Some more costly models envy the price-to-quality ratio here, and it’s mostly down to that very snazzy dual platter design.</p><p>Are there better-sounding turntables? Absolutely — but you’ll be paying hundreds if not thousands of dollars more to get the increase in performance from the likes of the Planar 3 RS, or even the Technics SL13000G. Great turntables that come for a hefty premium</p><p>This is a five-star deck through and through. My few nitpicks are few and far between, and don’t dull the luster of what is otherwise a spectacular buy for $800 or so. Just remember that you’ll need to factor in a phono pre-amp (and maybe some powered speakers if you don’t own some already), and you’re home free.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sennheiser just launched new flagship earbuds with a feature the AirPods can't match ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Sennheiser has just revealed the latest version of its flagship in-ear headphones, the Momentum 5 True Wireless earbuds. Taking the fight to the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless headphones</a> from Bose, Sony and even Apple's <a href="https://www.tomsguide.com/buying-guide/best-airpods">all-conquering AirPods</a>, the Momentum 5 have a notable sustainability feature that immediately caught my eye. </p><p>Alongside the audio upgrades (these buds feature Sennheiser's signature 7mm TrueResponse dynamic drivers) is a user-replaceable battery system in both the earbuds and the charging case. At a time when repairability is sorely needed, it's great to see a flagship product launch with the ability to swap the batteries "using only a compact screwdriver."</p><p>Lilika Beck, President of Sennheiser Consumer Hearing, said: “Balancing compact size, our signature high-fidelity acoustics, and a replaceable battery is a major breakthrough.</p><p>“With Momentum True Wireless 5, we are proving that cutting-edge performance does not have to sacrifice longevity.”</p><div><blockquote><p>Enabling the user to swap out batteries is a huge step in the right direction and it's something I'd like to see other manufacturers implement wherever possible.</p></blockquote></div><p>Enabling the user to swap out batteries is a huge step in the right direction and it's something I'd like to see other manufacturers implement wherever possible.</p><p>When it comes to the audio stuff, I'm not concerned. We gave the <a href="https://www.tomsguide.com/audio/earbuds/sennheiser-momentum-true-wireless-4">Sennheiser Momentum True Wireless 4 a 4.5 star review</a> a couple of years ago and I expect things will have improved since then. The new earbuds come packing Bluetooth 6.0 with aptX Adaptive codec, meaning you'll get hi-res wireless audio up to 24-bit/96 kHz, provided your streaming service supports it.</p><h2 id="battery-life-claims">Battery life claims</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:7989px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="54JEajSrbuKk9xNJAiQ7PZ" name="Senn_White_MTW5_Biker_Content10_Full_Format_sRGB" alt="Sennheiser Momentum 5 Wireless" src="https://cdn.mos.cms.futurecdn.net/54JEajSrbuKk9xNJAiQ7PZ.jpg" mos="" align="middle" fullscreen="" width="7989" height="4494" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sennheiser)</span></figcaption></figure><p>We can't confirm the battery life until we've tested them, but according to Sennheiser you'll get Up to 12 hours of playback per charge and 40 hours total with the charging case. That's with the ANC turned off, however. If you enable noise cancelling the battery life drops to six hours from the buds, with a further 20-25 hours supplied by the case. This is one area where Apple has Sennheiser beat: the AirPods Pro 3 will give you up to eight hours with ANC enabled.</p><p>Sennheiser says it has given the buds an all-new ergonomic design that improves comfort and portability. And users will be able to fire up Sennheiser's Smart Control Plus app to utilize an 8-band EQ and advanced personalization options. Which is a good thing as our review team is always unimpressed with earbuds that don't allow you to adapt the EQ.</p><p>Whilst these certainly aren't sports headphones, they're IP54 rated water and dust-resistant, so you can expect some protection against sweat or rainfall. Which is impressive when you consider you can replace the batteries yourself.</p><p>The Sennheiser Momentum True Wireless 5 <strong>will be available from September 3</strong> in a choice of Graphite, Cream, Copper, Denim or Lavender finishes. <strong>Pricing is set at $299 / £249 / AU$479 and CA$449</strong>.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XmxgBO"></div>                            </div>                            <script src="https://kwizly.com/embed/XmxgBO.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/airpods/airpods-ultra-everything-we-know-so-far-about-apples-camera-earbuds">AirPods Ultra: Everything we know so far about Apple's camera earbuds</a></li><li><a href="https://www.tomsguide.com/phones/iphones/ios-27s-safety-filter-was-ruining-my-facetime-calls-heres-how-i-turned-it-off">iOS 27’s safety filter was ruining my FaceTime calls — here’s how I turned it off</a></li><li><a href="https://www.tomsguide.com/tvs/best-buys-60th-anniversary-sale-is-live-here-are-my-top-9-tv-deals-on-qleds-and-oleds-starting-from-usd279">Best Buy's 60th Anniversary Sale is live — here are my top 9 TV deals on QLEDs and OLEDs starting from $279</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/sennheiser-just-launched-new-flagship-earbuds-with-a-feature-the-airpods-cant-match</link>
                                                                            <description>
                            <![CDATA[ Sennheiser has unveiled the Momentum 5 True Wireless earbuds with 6 hours of ANC playback and user-replaceable batteries. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">YPN6sTBQWkBtQmZ3gYuAHZ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/KX2AxfnxXzwwD6HNmWr8EP-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 20 Aug 2026 12:05:51 +0000</pubDate>                                                                                                                                <updated>Thu, 20 Aug 2026 12:10:34 +0000</updated>
                                                                                                                                            <category><![CDATA[Earbuds]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ jeff.parsons@futurenet.com (Jeff Parsons) ]]></author>                    <dc:creator><![CDATA[ Jeff Parsons ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/7z3UTGGrmSokMKxTWHmhjX.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Jeff is U.K. Editor-in-Chief for Tom’s Guide looking after the day-to-day output of the site’s British contingent. &lt;/p&gt;&lt;p&gt;Rising early and heading straight for the coffee machine, Jeff loves nothing more than dialling into the zeitgeist of the day’s tech news. A journalist for over a decade, he&#039;s travelled around the world testing and reviewing any gadget he can get his hands on.&lt;/p&gt;&lt;p&gt;Before joining the team at Tom’s Guide, Jeff covered technology and science for two of the U.K.’s biggest national news sites: Metro.co.uk and the Daily Mirror. Memorable moments include getting lost in Vienna in an electric Audi, touring Lockheed Martin’s mile-long jet factory in Fort Worth and filming a Netflix documentary about Elon Musk in West London.&lt;/p&gt;&lt;p&gt;When not plugged into the current news agenda, editing or commissioning a series of articles or debating the merits of Apple vs Android, Jeff can usually be found out for a run trying to shave precious seconds off his PB. Or lifting weights in a vain attempt to offset the ageing process.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/KX2AxfnxXzwwD6HNmWr8EP-1280-80.jpg">
                                                            <media:credit><![CDATA[Sennheiser]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sennheiser Momentum True Wireless 5]]></media:description>                                                            <media:text><![CDATA[Sennheiser Momentum True Wireless 5]]></media:text>
                                <media:title type="plain"><![CDATA[Sennheiser Momentum True Wireless 5]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/KX2AxfnxXzwwD6HNmWr8EP-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Sennheiser has just revealed the latest version of its flagship in-ear headphones, the Momentum 5 True Wireless earbuds. Taking the fight to the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless headphones</a> from Bose, Sony and even Apple's <a href="https://www.tomsguide.com/buying-guide/best-airpods">all-conquering AirPods</a>, the Momentum 5 have a notable sustainability feature that immediately caught my eye. </p><p>Alongside the audio upgrades (these buds feature Sennheiser's signature 7mm TrueResponse dynamic drivers) is a user-replaceable battery system in both the earbuds and the charging case. At a time when repairability is sorely needed, it's great to see a flagship product launch with the ability to swap the batteries "using only a compact screwdriver."</p><p>Lilika Beck, President of Sennheiser Consumer Hearing, said: “Balancing compact size, our signature high-fidelity acoustics, and a replaceable battery is a major breakthrough.</p><p>“With Momentum True Wireless 5, we are proving that cutting-edge performance does not have to sacrifice longevity.”</p><div><blockquote><p>Enabling the user to swap out batteries is a huge step in the right direction and it's something I'd like to see other manufacturers implement wherever possible.</p></blockquote></div><p>Enabling the user to swap out batteries is a huge step in the right direction and it's something I'd like to see other manufacturers implement wherever possible.</p><p>When it comes to the audio stuff, I'm not concerned. We gave the <a href="https://www.tomsguide.com/audio/earbuds/sennheiser-momentum-true-wireless-4">Sennheiser Momentum True Wireless 4 a 4.5 star review</a> a couple of years ago and I expect things will have improved since then. The new earbuds come packing Bluetooth 6.0 with aptX Adaptive codec, meaning you'll get hi-res wireless audio up to 24-bit/96 kHz, provided your streaming service supports it.</p><h2 id="battery-life-claims">Battery life claims</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:7989px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="54JEajSrbuKk9xNJAiQ7PZ" name="Senn_White_MTW5_Biker_Content10_Full_Format_sRGB" alt="Sennheiser Momentum 5 Wireless" src="https://cdn.mos.cms.futurecdn.net/54JEajSrbuKk9xNJAiQ7PZ.jpg" mos="" align="middle" fullscreen="" width="7989" height="4494" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sennheiser)</span></figcaption></figure><p>We can't confirm the battery life until we've tested them, but according to Sennheiser you'll get Up to 12 hours of playback per charge and 40 hours total with the charging case. That's with the ANC turned off, however. If you enable noise cancelling the battery life drops to six hours from the buds, with a further 20-25 hours supplied by the case. This is one area where Apple has Sennheiser beat: the AirPods Pro 3 will give you up to eight hours with ANC enabled.</p><p>Sennheiser says it has given the buds an all-new ergonomic design that improves comfort and portability. And users will be able to fire up Sennheiser's Smart Control Plus app to utilize an 8-band EQ and advanced personalization options. Which is a good thing as our review team is always unimpressed with earbuds that don't allow you to adapt the EQ.</p><p>Whilst these certainly aren't sports headphones, they're IP54 rated water and dust-resistant, so you can expect some protection against sweat or rainfall. Which is impressive when you consider you can replace the batteries yourself.</p><p>The Sennheiser Momentum True Wireless 5 <strong>will be available from September 3</strong> in a choice of Graphite, Cream, Copper, Denim or Lavender finishes. <strong>Pricing is set at $299 / £249 / AU$479 and CA$449</strong>.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XmxgBO"></div>                            </div>                            <script src="https://kwizly.com/embed/XmxgBO.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/airpods/airpods-ultra-everything-we-know-so-far-about-apples-camera-earbuds">AirPods Ultra: Everything we know so far about Apple's camera earbuds</a></li><li><a href="https://www.tomsguide.com/phones/iphones/ios-27s-safety-filter-was-ruining-my-facetime-calls-heres-how-i-turned-it-off">iOS 27’s safety filter was ruining my FaceTime calls — here’s how I turned it off</a></li><li><a href="https://www.tomsguide.com/tvs/best-buys-60th-anniversary-sale-is-live-here-are-my-top-9-tv-deals-on-qleds-and-oleds-starting-from-usd279">Best Buy's 60th Anniversary Sale is live — here are my top 9 TV deals on QLEDs and OLEDs starting from $279</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ AirPods Ultra: Everything we know so far about Apple's camera earbuds ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Apple's September event is here, and it's likely to bring us a new pair of AirPods, either the recently rumored <a href="https://www.tomsguide.com/audio/airpods/airpods-5-everything-weve-heard-so-far-about-apples-next-wireless-earbuds">AirPods 5</a> or the entirely new, cutting-edge AirPods Ultra.</p><p>Reporting from folks like Mark Gurman at Bloomberg claim that the so-called AirPods Ultra will leverage a new H3 chip, built-in cameras and Apple Intelligence to detect objects around you. They'll be a step in between the <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review">AirPods Pro 3</a> with heart-rate detection and more intrusive wearables like <a href="https://www.tomsguide.com/computing/smart-glasses/im-worried-smart-glasses-might-ruin-wearable-displays-and-of-course-its-the-cameras">Meta's "pervert glasses."</a> </p><p>So, what exactly do we know about these futuristic earbuds? Here's a full breakdown. </p><h2 id="airpods-ultra-potential-release-date">AirPods Ultra: Potential release date</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XvStwyvAJztT27hscB5xbP" name="AirPods Ultra" alt="AirPods Ultra being worn" src="https://cdn.mos.cms.futurecdn.net/XvStwyvAJztT27hscB5xbP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>The earliest reports of a camera-equipped pair of AirPods came from noted Apple analyst Ming-Chi Kuo. In a <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-with-cameras-tipped-for-2026-heres-what-they-could-do">report</a> dated July 1 2024, Kuo said that "the cameras can detect changes in the environment" and could "potentially enable in-air gesture control to enhance human-device interaction."</p><p>The legend of the camera-equipped AirPods Ultra has only grown from there. </p><p>These days, the person who seems to know the most about the AirPods Ultra is Bloomberg's Mark Gurman. Back in January of 2025, Gurman said in a newsletter that Apple was “exploring other types of wearable products” that included <a href="https://www.tomsguide.com/audio/earbuds/apple-reportedly-working-on-airpods-with-cameras-heres-what-they-could-do">a pair of AirPods that could come with miniature infrared cameras</a>. Back in June, he said that we're likely looking at a <a href="https://www.tomsguide.com/audio/airpods/gurman-ai-airpods-with-cameras-just-tipped-for-2027-launch-in-flurry-of-new-releases">2027 release date</a>. This was then repeated in a post on X.</p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2090214942494670853"><p lang="en" dir="ltr">I recently reported that Apple has two versions in development: B790 and B798. B790 is modified AirPods Pro 3 with cameras and B798 are AirPods Pro 4 with cameras. Neither are coming this year. B790 was more recently still on road map. https://t.co/gOwY2kBWFJ<a href="https://twitter.com/cantworkitout/status/2090214942494670853">August 19, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>It's possible Apple might unveil them at this September's <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-18-event-all-the-new-products-expected-to-launch-in-september">iPhone 18 event</a>, but I'm not entirely convinced. Apple is rarely in a rush to release a product, especially one as potentially complex as the AirPods Ultra are rumored to be, so I'm not exactly putting money on this. </p><p>The most likely scenario based on the reports I've read peg the AirPods 5 as the most likely candidate for the 2026 event and the AirPods Ultra for a Spring or Fall 2027 launch.</p><h2 id="airpods-ultra-potential-features">AirPods Ultra: Potential 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="XNJSXRYLTSowpk9PEQvoJo" name="apple airpods pro 3" alt="Apple airpods pro 3" src="https://cdn.mos.cms.futurecdn.net/XNJSXRYLTSowpk9PEQvoJo.jpg" 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 what are the AirPods Ultra going to do different from the current-gen AirPods Pro 3? Well, it turns out, quite a lot. </p><p>As far as we know right now, there are two different models in the works: There's a B790 model and a B798 model. The former is closer to launch than the latter, and it's unlikely that both will debut at the same time. </p><p>On the outside of the earbuds will be IR cameras that can scan objects around you using Apple's Visual Intelligence. (If you have long hair that impedes the view of one of these cameras, the earbuds will ask you to move it to the side.)</p><p>A video purportedly found in the latest macOS Tahoe 26.7 software shows what this could look like in action.</p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2089522745184760112"><p lang="en" dir="ltr">Here is your first look at AirPods with Cameras in action using Visual Intelligence.This video file came from macOS 26.7 RC pic.twitter.com/yo7RI4MCeu<a href="https://twitter.com/cantworkitout/status/2089522745184760112">August 18, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>The AirPods Ultra's IR cameras are, reportedly, lower resolution than Meta's AR glasses and will feature a light-up LED that will turn on when it's scanning, which should mean that they'll be less of a privacy concern than other wearables we've seen lately. </p><p>There are a number of potential uses for IR-enabled earbuds, including proximity detection to other people that can improve ANC functionality. </p><p>To power this functionality would be a new H3 chip. Little is known about it other than the fact that it is the key driver of the deeper Apple Intelligence integration. Better processors usually enable better noise cancelling, however, so expect to see that as a feature Apple touts sometime in the future, too.</p><h2 id="airpods-ultra-potential-price">AirPods Ultra: Potential 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RZQgGR565dk66rhS5XyBES" name="sony vs apple" alt="sony wf-1000xm6 vs apple airpods pro 3" src="https://cdn.mos.cms.futurecdn.net/RZQgGR565dk66rhS5XyBES.jpg" mos="" align="middle" fullscreen="" width="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>All this cutting-edge tech isn't likely to come cheap. A fair assumption right out of the gate is that the AirPods Ultra will cost more than the current-generation <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review">AirPods Pro 3</a> that currently sit at $249 / £249 / AU$429.</p><p>I would not be surprised if Apple charged $349, which would be $100 more than the AirPods Pro 3. There is some wiggle room if Apple wants to increase the sticker price of its new earbuds: Two of the AirPods' biggest rivals, the <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-1000xm6-review">Sony WF-1000XM6</a> and <a href="https://www.tomsguide.com/audio/earbuds/bose-quietcomfort-ultra-earbuds-2-review">Bose QuietComfort Ultra Earbuds 2</a>, cost $299. </p><p>For now, all we can do is prognosticate what Apple will do with pricing as the AirPods Ultra are truly a one-of-a-kind device that doesn't really match up with anything on the market at the moment.</p><h2 id="airpods-ultra-outlook-why-i-39-m-skeptical">AirPods Ultra outlook: Why I'm skeptical</h2><p>The AirPods Ultra do sound like an extremely inventive innovation in the world of wireless earbuds. The ability for a pair of earbuds to detect objects around a user has major implications for the future of wearable technology. </p><p>However, I have two major reservations about the AirPods Ultra. </p><p>The first is that it's unlike Apple to jump first when it comes to launching a new type of product. Apple wasn't the first AR glasses manufacturer, for instance, and it certainly wasn't the first to make an MP3 player or laptop. What Apple usually does is that it lets competitors lead and then presents a more elegant solution. </p><p>Even if the AirPods Ultra are production-ready, that doesn't mean Apple will put them out without waiting for something else on the market first.</p><p>I'm also skeptical about the cameras themselves. Apple paints itself as extremely privacy focused. Putting cameras on a pair of earbuds flies in the face of that stance. Unless there are some serious limitations of the technology (like the only data it collects are IR images), it'll be hard for Apple to keep its privacy-first reputation. </p><p>But if Apple finds the balance between features and privacy, locks in at a reasonable price and gets developers on board, the AirPods Ultra could truly be the next big thing in wireless earbuds. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZQjKO"></div>                            </div>                            <script src="https://kwizly.com/embed/XZQjKO.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/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know">AirPods Ultra with cameras are 'nearly ready' — here's 4 features you need to know</a></li><li><a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-almost-certainly-hinted-at-in-ios-27-code-and-they-could-be-a-huge-audio-and-camera-upgrade">AirPods Ultra 'almost certainly' hinted at in iOS 27 code — and they could be a huge audio (and camera) upgrade</a></li><li><a href="https://www.tomsguide.com/phones/iphones/apple-iphone-18-event-all-the-new-products-expected-to-launch-in-september">Apple iPhone 18 event — all the new products expected to launch in September</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/airpods/airpods-ultra-everything-we-know-so-far-about-apples-camera-earbuds</link>
                                                                            <description>
                            <![CDATA[ 2026 is the 10th anniversary of the original Apple AirPods — and that could mean a special surprise for Apple's true wireless earbuds line. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">3jRPwnDRPTKWhhGYEajo4o</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/aGsV2oXwfxKSe7BhHdpZuP-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 20 Aug 2026 11:00:00 +0000</pubDate>                                                                                                                                <updated>Tue, 08 Sep 2026 19:09:53 +0000</updated>
                                                                                                                                            <category><![CDATA[Airpods]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></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.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/aGsV2oXwfxKSe7BhHdpZuP-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Smartphone and AirPods]]></media:description>                                                            <media:text><![CDATA[Smartphone and AirPods]]></media:text>
                                <media:title type="plain"><![CDATA[Smartphone and AirPods]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/aGsV2oXwfxKSe7BhHdpZuP-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Apple's September event is here, and it's likely to bring us a new pair of AirPods, either the recently rumored <a href="https://www.tomsguide.com/audio/airpods/airpods-5-everything-weve-heard-so-far-about-apples-next-wireless-earbuds">AirPods 5</a> or the entirely new, cutting-edge AirPods Ultra.</p><p>Reporting from folks like Mark Gurman at Bloomberg claim that the so-called AirPods Ultra will leverage a new H3 chip, built-in cameras and Apple Intelligence to detect objects around you. They'll be a step in between the <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review">AirPods Pro 3</a> with heart-rate detection and more intrusive wearables like <a href="https://www.tomsguide.com/computing/smart-glasses/im-worried-smart-glasses-might-ruin-wearable-displays-and-of-course-its-the-cameras">Meta's "pervert glasses."</a> </p><p>So, what exactly do we know about these futuristic earbuds? Here's a full breakdown. </p><h2 id="airpods-ultra-potential-release-date">AirPods Ultra: Potential release date</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XvStwyvAJztT27hscB5xbP" name="AirPods Ultra" alt="AirPods Ultra being worn" src="https://cdn.mos.cms.futurecdn.net/XvStwyvAJztT27hscB5xbP.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>The earliest reports of a camera-equipped pair of AirPods came from noted Apple analyst Ming-Chi Kuo. In a <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-with-cameras-tipped-for-2026-heres-what-they-could-do">report</a> dated July 1 2024, Kuo said that "the cameras can detect changes in the environment" and could "potentially enable in-air gesture control to enhance human-device interaction."</p><p>The legend of the camera-equipped AirPods Ultra has only grown from there. </p><p>These days, the person who seems to know the most about the AirPods Ultra is Bloomberg's Mark Gurman. Back in January of 2025, Gurman said in a newsletter that Apple was “exploring other types of wearable products” that included <a href="https://www.tomsguide.com/audio/earbuds/apple-reportedly-working-on-airpods-with-cameras-heres-what-they-could-do">a pair of AirPods that could come with miniature infrared cameras</a>. Back in June, he said that we're likely looking at a <a href="https://www.tomsguide.com/audio/airpods/gurman-ai-airpods-with-cameras-just-tipped-for-2027-launch-in-flurry-of-new-releases">2027 release date</a>. This was then repeated in a post on X.</p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2090214942494670853"><p lang="en" dir="ltr">I recently reported that Apple has two versions in development: B790 and B798. B790 is modified AirPods Pro 3 with cameras and B798 are AirPods Pro 4 with cameras. Neither are coming this year. B790 was more recently still on road map. https://t.co/gOwY2kBWFJ<a href="https://twitter.com/cantworkitout/status/2090214942494670853">August 19, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>It's possible Apple might unveil them at this September's <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-18-event-all-the-new-products-expected-to-launch-in-september">iPhone 18 event</a>, but I'm not entirely convinced. Apple is rarely in a rush to release a product, especially one as potentially complex as the AirPods Ultra are rumored to be, so I'm not exactly putting money on this. </p><p>The most likely scenario based on the reports I've read peg the AirPods 5 as the most likely candidate for the 2026 event and the AirPods Ultra for a Spring or Fall 2027 launch.</p><h2 id="airpods-ultra-potential-features">AirPods Ultra: Potential 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="XNJSXRYLTSowpk9PEQvoJo" name="apple airpods pro 3" alt="Apple airpods pro 3" src="https://cdn.mos.cms.futurecdn.net/XNJSXRYLTSowpk9PEQvoJo.jpg" 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 what are the AirPods Ultra going to do different from the current-gen AirPods Pro 3? Well, it turns out, quite a lot. </p><p>As far as we know right now, there are two different models in the works: There's a B790 model and a B798 model. The former is closer to launch than the latter, and it's unlikely that both will debut at the same time. </p><p>On the outside of the earbuds will be IR cameras that can scan objects around you using Apple's Visual Intelligence. (If you have long hair that impedes the view of one of these cameras, the earbuds will ask you to move it to the side.)</p><p>A video purportedly found in the latest macOS Tahoe 26.7 software shows what this could look like in action.</p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2089522745184760112"><p lang="en" dir="ltr">Here is your first look at AirPods with Cameras in action using Visual Intelligence.This video file came from macOS 26.7 RC pic.twitter.com/yo7RI4MCeu<a href="https://twitter.com/cantworkitout/status/2089522745184760112">August 18, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>The AirPods Ultra's IR cameras are, reportedly, lower resolution than Meta's AR glasses and will feature a light-up LED that will turn on when it's scanning, which should mean that they'll be less of a privacy concern than other wearables we've seen lately. </p><p>There are a number of potential uses for IR-enabled earbuds, including proximity detection to other people that can improve ANC functionality. </p><p>To power this functionality would be a new H3 chip. Little is known about it other than the fact that it is the key driver of the deeper Apple Intelligence integration. Better processors usually enable better noise cancelling, however, so expect to see that as a feature Apple touts sometime in the future, too.</p><h2 id="airpods-ultra-potential-price">AirPods Ultra: Potential 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RZQgGR565dk66rhS5XyBES" name="sony vs apple" alt="sony wf-1000xm6 vs apple airpods pro 3" src="https://cdn.mos.cms.futurecdn.net/RZQgGR565dk66rhS5XyBES.jpg" mos="" align="middle" fullscreen="" width="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>All this cutting-edge tech isn't likely to come cheap. A fair assumption right out of the gate is that the AirPods Ultra will cost more than the current-generation <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review">AirPods Pro 3</a> that currently sit at $249 / £249 / AU$429.</p><p>I would not be surprised if Apple charged $349, which would be $100 more than the AirPods Pro 3. There is some wiggle room if Apple wants to increase the sticker price of its new earbuds: Two of the AirPods' biggest rivals, the <a href="https://www.tomsguide.com/audio/earbuds/sony-wf-1000xm6-review">Sony WF-1000XM6</a> and <a href="https://www.tomsguide.com/audio/earbuds/bose-quietcomfort-ultra-earbuds-2-review">Bose QuietComfort Ultra Earbuds 2</a>, cost $299. </p><p>For now, all we can do is prognosticate what Apple will do with pricing as the AirPods Ultra are truly a one-of-a-kind device that doesn't really match up with anything on the market at the moment.</p><h2 id="airpods-ultra-outlook-why-i-39-m-skeptical">AirPods Ultra outlook: Why I'm skeptical</h2><p>The AirPods Ultra do sound like an extremely inventive innovation in the world of wireless earbuds. The ability for a pair of earbuds to detect objects around a user has major implications for the future of wearable technology. </p><p>However, I have two major reservations about the AirPods Ultra. </p><p>The first is that it's unlike Apple to jump first when it comes to launching a new type of product. Apple wasn't the first AR glasses manufacturer, for instance, and it certainly wasn't the first to make an MP3 player or laptop. What Apple usually does is that it lets competitors lead and then presents a more elegant solution. </p><p>Even if the AirPods Ultra are production-ready, that doesn't mean Apple will put them out without waiting for something else on the market first.</p><p>I'm also skeptical about the cameras themselves. Apple paints itself as extremely privacy focused. Putting cameras on a pair of earbuds flies in the face of that stance. Unless there are some serious limitations of the technology (like the only data it collects are IR images), it'll be hard for Apple to keep its privacy-first reputation. </p><p>But if Apple finds the balance between features and privacy, locks in at a reasonable price and gets developers on board, the AirPods Ultra could truly be the next big thing in wireless earbuds. </p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZQjKO"></div>                            </div>                            <script src="https://kwizly.com/embed/XZQjKO.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/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know">AirPods Ultra with cameras are 'nearly ready' — here's 4 features you need to know</a></li><li><a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-almost-certainly-hinted-at-in-ios-27-code-and-they-could-be-a-huge-audio-and-camera-upgrade">AirPods Ultra 'almost certainly' hinted at in iOS 27 code — and they could be a huge audio (and camera) upgrade</a></li><li><a href="https://www.tomsguide.com/phones/iphones/apple-iphone-18-event-all-the-new-products-expected-to-launch-in-september">Apple iPhone 18 event — all the new products expected to launch in September</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Amazon's headphone sale is packed with deals from $19 — here are the 15 I'd buy ]]></title>
                                                                                                <dc:content><![CDATA[ <p>With summer winding down and back to school season in full swing, there's no better time than the present to upgrade your audio devices. Even better, Amazon is currently knocking up to 50% off top-rated headphones and earbuds.  </p><p>Whether you're looking for noise-cancelling headphones to help you focus during your study session or a pair of wireless earbuds for when you're hitting the gym, Amazon's sale has deals for every budget. Some of the <a href="https://www.tomsguide.com/us/best-headphones-deals,news-28645.html">best headphones </a>we've tested from brands like JBL, Bose, Apple, and Sony are discounted to as little as $19.</p><p>I've rounded up all my favorite headphone and earbud deals on Amazon that will get you the most bang for your buck! Keep scrolling to check them out. For more deals, check out the <a href="https://www.tomsguide.com/savings-squad">Tom's Guide Savings Squad</a> and the <a href="https://www.amazon.com/shop/tomsguide" target="_blank" rel="nofollow">Tom's Guide Amazon Storefront</a>.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links </span></h3><ul><li><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>shop all deals on Amazon</strong></a></li><li><strong>Anker Soundcore P20i: </strong><a href="https://www.amazon.com/dp/B0BTYCRJSS" target="_blank" rel="nofollow"><strong>was $39 now $19</strong></a></li><li><strong>Sony WH-CH520: </strong><a href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow"><strong>was $59 now $38</strong></a></li><li><strong>Beats Flex Wireless: </strong><a href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow"><strong>was $69 now $45</strong></a></li><li><strong>Apple AirPods 4: </strong><a href="https://www.amazon.com/dp/B0DGHMNQ5Z/" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a><strong></strong></li><li><strong>Beats Studio Pro: </strong><a href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" target="_blank" rel="nofollow"><strong>was $349 now $169</strong></a></li><li><strong>Sony WH-1000XM5: </strong><a href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B09XS7JWHH?" target="_blank" rel="nofollow"><strong>was $399 now $248</strong></a></li><li><strong>Bose QuietComfort Ultra: </strong><a href="https://www.amazon.com/dp/B0CCZ1L489?" target="_blank" rel="nofollow"><strong>was $449 now $364</strong></a></li></ul><h3 class="article-body__section" id="section-earbud-deals"><span>Earbud Deals</span></h3><div class="product"><a data-dimension112="a9263cc0-9bfe-11f1-810b-ed37eb18eeb3" data-action="Deal Block" data-label="P20i" data-dimension48="P20i" data-dimension25="$19" href="https://www.amazon.com/dp/B0BTYCRJSS" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:800px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="gXu4CaGwCDNUjS7yXv2cP4" name="Soundcore by Anker P20i True Wireless Earbuds.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/gXu4CaGwCDNUjS7yXv2cP4.jpg" mos="" align="middle" fullscreen="" width="800" height="800" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you're willing to live without active noise-cancellation, these Soundcore by Anker P20i buds are fantastic value for money. Offering 10mm drivers for impressive "big" bass, along with Bluetooth 5.3, 30-hour battery life, water-resistance, 22 preset EQs and in-app customisation, which is great for this price.</p></div><div class="product"><a data-dimension112="a9263d24-9bfe-11f1-9f64-d3e8bbe558d1" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45" href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:103.68%;"><img id="FGXTiRGeZrNnRV5M5SvbR9" name="Beats Flex" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FGXTiRGeZrNnRV5M5SvbR9.jpg" mos="" align="middle" fullscreen="" width="679" height="704" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow" data-dimension112="a9263d24-9bfe-11f1-9f64-d3e8bbe558d1" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45">Flex Wireless: was $69 now $45</a></strong><br>While they aren't <em>technically </em>true wireless, on account of the wire that connects the buds, the Flex are a great pair of fitness earbuds. They're super comfortable thanks to their lightweight, and this $24 saving is a solid discount.<a class="view-deal button" href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow" data-dimension112="a9263d24-9bfe-11f1-9f64-d3e8bbe558d1" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45">View Deal</a></p></div><div class="product"><a data-dimension112="a9263d92-9bfe-11f1-8d11-5d0f1fe8ad68" data-action="Deal Block" data-label="AirPods 4" data-dimension48="AirPods 4" data-dimension25="$99" href="https://www.amazon.com/dp/B0DGHMNQ5Z/" 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="kTKCG8gCBjg4vScr6CMgoN" name="AirPods 4 BG image.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kTKCG8gCBjg4vScr6CMgoN.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods 4 are about as barebones as AirPods come, but they're still packed with features. Spatial Audio is on board for example, something you'd have to pay more for on the old model. 5 hours of battery isn't the very best, but they're a great on boarding place for the AirPods line.<a class="view-deal button" href="https://www.amazon.com/dp/B0DGHMNQ5Z/" target="_blank" rel="nofollow" data-dimension112="a9263d92-9bfe-11f1-8d11-5d0f1fe8ad68" data-action="Deal Block" data-label="AirPods 4" data-dimension48="AirPods 4" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="a9263e00-9bfe-11f1-8fa5-f937c009ca4d" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99" href="https://www.amazon.com/dp/B0C2W1KLSS/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:107.51%;"><img id="Nu3NHDhGaSDd5XGizimnyT" name="Studio Buds plus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Nu3NHDhGaSDd5XGizimnyT.jpg" mos="" align="middle" fullscreen="" width="679" height="730" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Studio Buds are a wonderful little pair of buds, with some big improvements over the last model. 9 hours of battery life is exemplary, extended to 36 hours with the charging case, and they're some of the most comfortable wireless in-ears around. In our <a href="https://www.tomsguide.com/reviews/beats-studio-buds-plus" data-dimension112="a9263e00-9bfe-11f1-8fa5-f937c009ca4d" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99">Beats Studio Buds Plus review</a>, we gave them 4 stars thanks to their great design and sound.<a class="view-deal button" href="https://www.amazon.com/dp/B0C2W1KLSS/" target="_blank" rel="nofollow" data-dimension112="a9263e00-9bfe-11f1-8fa5-f937c009ca4d" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="a9263e64-9bfe-11f1-98bd-1d02738061ca" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$189" href="https://www.amazon.com/Apple-Cancellation-Translation-Headphones-High-Fidelity/dp/B0FQFB8FMG" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:842px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="FYzcjQSdtuwidVacXmMNL5" name="AirPods Pro 3.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FYzcjQSdtuwidVacXmMNL5.jpg" mos="" align="middle" fullscreen="" width="842" height="842" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods Pro 3 bring the heat with 10 hours of battery life, improved sound, and superior active noise canceling. In our <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review" data-dimension112="a9263e64-9bfe-11f1-98bd-1d02738061ca" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$189">AirPods Pro 3 review</a> we said the Editor's Choice buds deliver the upgrade iPhone users have been waiting for with improved battery life, stronger noise cancellation, and smart new features like Live Translation, which delivers real-time translations to your ears. <a class="view-deal button" href="https://www.amazon.com/Apple-Cancellation-Translation-Headphones-High-Fidelity/dp/B0FQFB8FMG" target="_blank" rel="nofollow" data-dimension112="a9263e64-9bfe-11f1-98bd-1d02738061ca" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$189">View Deal</a></p></div><div class="product"><a data-dimension112="a9263ed2-9bfe-11f1-98c4-35a5eec45c15" data-action="Deal Block" data-label="Powerbeats Pro" data-dimension48="Powerbeats Pro" data-dimension25="$199" href="https://www.amazon.com/dp/B07R5QD598?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:914px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="U583dQHPqNRENp8PNfSbRU" name="Beats Powerbeats Pro_2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/U583dQHPqNRENp8PNfSbRU.jpg" mos="" align="middle" fullscreen="" width="914" height="914" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/us/powerbeats-pro,review-6461.html" data-dimension112="a9263ed2-9bfe-11f1-98c4-35a5eec45c15" data-action="Deal Block" data-label="Powerbeats Pro" data-dimension48="Powerbeats Pro" data-dimension25="$199">Powerbeats Pro</a> are a solid pair of Apple wireless fitness earbuds. They have a seamless setup, just like AirPods, and good battery life. The earbuds themselves can play music for 9 hours, and the charging case adds an additional 18 hours. If you have an iPhone and need earbuds for your workout, these are a great pick — not that they aren't great for Android, too.<a class="view-deal button" href="https://www.amazon.com/dp/B07R5QD598?" target="_blank" rel="nofollow" data-dimension112="a9263ed2-9bfe-11f1-98c4-35a5eec45c15" data-action="Deal Block" data-label="Powerbeats Pro" data-dimension48="Powerbeats Pro" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="a9263f36-9bfe-11f1-8dd6-63c847872543" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216" href="https://www.amazon.com/dp/B0CPFVB5VN?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1268px;"><p class="vanilla-image-block" style="padding-top:62.38%;"><img id="qDBpkjESq4Z3UsvHc4vEeX" name="bose open earbuds" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qDBpkjESq4Z3UsvHc4vEeX.jpg" mos="" align="middle" fullscreen="" width="1268" height="791" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CPFVB5VN?" target="_blank" rel="nofollow" data-dimension112="a9263f36-9bfe-11f1-8dd6-63c847872543" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216">Ultra Open Earbuds: was $299 now $216</a></strong><br>The Bose Ultra Open perfected the open earbud formula. They have a comfortable, non-invasive fit, solid battery life, and they sound better than you might expect from open buds. They got <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds">4 stars in our review</a>, where we loved their simple controls and surprising lack of sound leakage. <a class="view-deal button" href="https://www.amazon.com/dp/B0CPFVB5VN?" target="_blank" rel="nofollow" data-dimension112="a9263f36-9bfe-11f1-8dd6-63c847872543" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216">View Deal</a></p></div><div class="product"><a data-dimension112="a9263fa4-9bfe-11f1-828a-bbed02ddd7fb" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248" href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1509px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="zpDUbSJksxwfgT3KDaf4WS" name="Sony WF-1000XM5.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zpDUbSJksxwfgT3KDaf4WS.jpg" mos="" align="middle" fullscreen="" width="1509" height="1509" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow" data-dimension112="a9263fa4-9bfe-11f1-828a-bbed02ddd7fb" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248">WF-1000XM5: was $329 now $248</a></strong><br>These are some of the best buds that you can buy right now. They sound excellent, with some wickedly detailed sound with plenty of low-end grunt, and feature a very comfortable fit. We gave them 4.5 stars in <a href="https://www.tomsguide.com/reviews/sony-wf-1000xm5">our review</a> because of their great ANC, which manages to block out almost any noise you could throw at them. This isn't <em>quite</em> their lowest price ever, but it's very close.<a class="view-deal button" href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow" data-dimension112="a9263fa4-9bfe-11f1-828a-bbed02ddd7fb" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248">View Deal</a></p></div><h3 class="article-body__section" id="section-headphone-deals"><span>Headphone Deals</span></h3><div class="product"><a data-dimension112="a926406c-9bfe-11f1-954c-f9323b18a28b" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38" href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:510px;"><p class="vanilla-image-block" style="padding-top:154.90%;"><img id="bzH89XJXSHScmHcf8rgiyP" name="Sony_WH-C520_Headphones_cappucino_deal_block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bzH89XJXSHScmHcf8rgiyP.jpg" mos="" align="middle" fullscreen="" width="510" height="790" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Our <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-ch520-review" data-dimension112="a926406c-9bfe-11f1-954c-f9323b18a28b" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38">Sony WH-CH520 headphones review</a> said these headphones "deliver on battery life and performance."  They're 360 Reality Audio compatible for more 3D sound and you can customize EQ in the Sony Headphones app. Although there's no active noise cancelling function, they have built-in mics for hands-free voice and video calls and noise suppression processing to keep your voice sounding clear in noisy environments. They're also rated for up to 50 hours of battery life.<a class="view-deal button" href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow" data-dimension112="a926406c-9bfe-11f1-954c-f9323b18a28b" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38">View Deal</a></p></div><div class="product"><a data-dimension112="a92640da-9bfe-11f1-b818-99c01861ca49" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59" href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1190px;"><p class="vanilla-image-block" style="padding-top:113.61%;"><img id="kYA8RY5ohsqVvc9Ki7zKfL" name="JBL Tune 720BT.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kYA8RY5ohsqVvc9Ki7zKfL.jpg" mos="" align="middle" fullscreen="" width="1190" height="1352" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow" data-dimension112="a92640da-9bfe-11f1-b818-99c01861ca49" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59">Tune 720BT: was $89 now $59</a></strong><br>These budget cans pack in more features, although you'll miss out on the ANC you'll get with some of the other options on this list. Still, these headphones have a massive 76 hour battery, so they'll last you for a long, long time. Thick cushioning provides a comfortable fit for long periods of wear.<a class="view-deal button" href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow" data-dimension112="a92640da-9bfe-11f1-b818-99c01861ca49" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59">View Deal</a></p></div><div class="product"><a data-dimension112="a9264148-9bfe-11f1-8921-1ff0611ea68b" data-action="Deal Block" data-label="Solo 4" data-dimension48="Solo 4" data-dimension25="$129" href="https://www.amazon.com/dp/B0CZPHPJLN/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:717px;"><p class="vanilla-image-block" style="padding-top:154.25%;"><img id="ZDYGygzUQQsApUVGtH5RBe" name="510fGxoTsTL._AC_SL1500_.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ZDYGygzUQQsApUVGtH5RBe.jpg" mos="" align="middle" fullscreen="" width="717" height="1106" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Solo 4 might not feature noise canceling, but they make up for it by sounding pretty good. They're comfortable as well thanks to Beats new foam, and they fold up super small so that they can fit into any bag. There's also loads of battery life — 50 hours, to be exact.<a class="view-deal button" href="https://www.amazon.com/dp/B0CZPHPJLN/" target="_blank" rel="nofollow" data-dimension112="a9264148-9bfe-11f1-8921-1ff0611ea68b" data-action="Deal Block" data-label="Solo 4" data-dimension48="Solo 4" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="a92641ac-9bfe-11f1-a0af-0bbee676d453" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169" href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" 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="WbdEkaq3pnr5qszwU9U9aX" name="Beats Studio Pro on white.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WbdEkaq3pnr5qszwU9U9aX.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Studio Pro are specifically designed to bring back that thumping bass Beats is known for. They offer improved active noise cancelation and boosted battery life (up to 40 hours with ANC turned off). There's also Spatial Audio support and Beats' signature strong brand look. However, our <a href="https://www.tomsguide.com/reviews/beats-studio-pro-review" data-dimension112="a92641ac-9bfe-11f1-a0af-0bbee676d453" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169">Beats Studio Pro review</a> found the clamping force high, which may affect comfort levels for some wearers. Originally priced at $349, they've now on sale for over half off.<a class="view-deal button" href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" target="_blank" rel="nofollow" data-dimension112="a92641ac-9bfe-11f1-a0af-0bbee676d453" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169">View Deal</a></p></div><div class="product"><a data-dimension112="a926421a-9bfe-11f1-9940-1b87e9849042" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248" href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B09XS7JWHH?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1381px;"><p class="vanilla-image-block" style="padding-top:108.62%;"><img id="TWLQcjD9oitTFCdEHsgTe5" name="Sony WH-1000XM5 deal block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TWLQcjD9oitTFCdEHsgTe5.jpg" mos="" align="middle" fullscreen="" width="1381" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B09XS7JWHH?" target="_blank" rel="nofollow" data-dimension112="a926421a-9bfe-11f1-9940-1b87e9849042" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248">WH-1000XM5: was $399 now $248</a></strong><br>The XM5s are Sony's premium noise-canceling headphones and our pick for the best headphones on the market. The large, over-ear cushions are incredibly comfortable and the active noise cancelation is excellent. Plus, despite ANC being a drain on battery life, these amazing headphones still give you up to 30 hours with ANC enabled. You can push that up to 40 hours of battery life with ANC turned off.<a class="view-deal button" href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B09XS7JWHH?" target="_blank" rel="nofollow" data-dimension112="a926421a-9bfe-11f1-9940-1b87e9849042" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248">View Deal</a></p></div><div class="product"><a data-dimension112="a926427e-9bfe-11f1-8f87-b57fbf353032" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$364" href="https://www.amazon.com/dp/B0CCZ1L489?" 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="g64SDV3bmD2tcgopiiNgP6" name="Bose QuietComfort Ultra headphones Versus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/g64SDV3bmD2tcgopiiNgP6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Headphones get you epic ANC, super sound, and the most comfortable fit this side of wrapping your head in a snood that plays music. 24 hours of battery life isn't the best, but they make up for it with just about everything else.<a class="view-deal button" href="https://www.amazon.com/dp/B0CCZ1L489?" target="_blank" rel="nofollow" data-dimension112="a926427e-9bfe-11f1-8f87-b57fbf353032" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$364">View Deal</a></p></div><div class="product"><a data-dimension112="a92642ec-9bfe-11f1-ab86-5d4657ada425" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$429" href="https://www.amazon.com/dp/B0GSS4SGZR?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1356px;"><p class="vanilla-image-block" style="padding-top:110.62%;"><img id="augarq3rWrbuXENFrmUU88" name="airpods max 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/augarq3rWrbuXENFrmUU88.jpg" mos="" align="middle" fullscreen="" width="1356" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods Max 2 are Apple's latest flagship over-ears, with hi-res audio playback via USB-C and Bluetooth, seamless Siri integration and smart features like Live Translation, intuitive controls, and a sleek, iconic design.<a class="view-deal button" href="https://www.amazon.com/dp/B0GSS4SGZR?" target="_blank" rel="nofollow" data-dimension112="a92642ec-9bfe-11f1-ab86-5d4657ada425" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$429">View Deal</a></p></div><h3 class="article-body__section" id="section-shop-more-deals"><span>Shop More Deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Headphones&min_discount_ratio=0.95&offer_type=all&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("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/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/audio/amazons-headphone-sale-is-packed-with-deals-from-usd19-here-are-the-15-id-buy</link>
                                                                            <description>
                            <![CDATA[ Amazon is knocking up to 50% off headphones and earbuds. Check out my favorite affordable picks from Bose, Apple, Sony and more. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">oeXcxpnB8Q9igXrQCC3h7Y</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/7wLqZcwbmtG4uVPwXApnVG-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 20 Aug 2026 09:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Olivia Halevy ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/YAg7Fmh9dGKbg3hULBMWiU.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Olivia is a Deals Writer at Tom’s Guide. She loves all things lifestyle, shopping and sourcing the best deals from some of the top brands, retailers and websites around. From fashion and beauty to smart home and tech, she has an eye for finding practical, stylish, and reasonably-priced items that help make life simpler (and a little more aesthetically-pleasing).&amp;nbsp;Olivia has over five years of experience writing lifestyle, commerce, and branded content for &lt;a href=&quot;http://patch.com/&quot;&gt;Patch.com&lt;/a&gt;. She&#039;s covered major shopping holidays like Amazon Prime Day, Black Friday and Cyber Monday. In her free time, she enjoys trying out new exercise classes, binge-watching the latest viral TV shows, cooking, spending time with family and taking long walks with her dog, Gus.&amp;nbsp;&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/7wLqZcwbmtG4uVPwXApnVG-1280-80.jpg">
                                                            <media:credit><![CDATA[Amazon]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Headphones and earbuds]]></media:description>                                                            <media:text><![CDATA[Headphones and earbuds]]></media:text>
                                <media:title type="plain"><![CDATA[Headphones and earbuds]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/7wLqZcwbmtG4uVPwXApnVG-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>With summer winding down and back to school season in full swing, there's no better time than the present to upgrade your audio devices. Even better, Amazon is currently knocking up to 50% off top-rated headphones and earbuds.  </p><p>Whether you're looking for noise-cancelling headphones to help you focus during your study session or a pair of wireless earbuds for when you're hitting the gym, Amazon's sale has deals for every budget. Some of the <a href="https://www.tomsguide.com/us/best-headphones-deals,news-28645.html">best headphones </a>we've tested from brands like JBL, Bose, Apple, and Sony are discounted to as little as $19.</p><p>I've rounded up all my favorite headphone and earbud deals on Amazon that will get you the most bang for your buck! Keep scrolling to check them out. For more deals, check out the <a href="https://www.tomsguide.com/savings-squad">Tom's Guide Savings Squad</a> and the <a href="https://www.amazon.com/shop/tomsguide" target="_blank" rel="nofollow">Tom's Guide Amazon Storefront</a>.</p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links </span></h3><ul><li><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>shop all deals on Amazon</strong></a></li><li><strong>Anker Soundcore P20i: </strong><a href="https://www.amazon.com/dp/B0BTYCRJSS" target="_blank" rel="nofollow"><strong>was $39 now $19</strong></a></li><li><strong>Sony WH-CH520: </strong><a href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow"><strong>was $59 now $38</strong></a></li><li><strong>Beats Flex Wireless: </strong><a href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow"><strong>was $69 now $45</strong></a></li><li><strong>Apple AirPods 4: </strong><a href="https://www.amazon.com/dp/B0DGHMNQ5Z/" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a><strong></strong></li><li><strong>Beats Studio Pro: </strong><a href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" target="_blank" rel="nofollow"><strong>was $349 now $169</strong></a></li><li><strong>Sony WH-1000XM5: </strong><a href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B09XS7JWHH?" target="_blank" rel="nofollow"><strong>was $399 now $248</strong></a></li><li><strong>Bose QuietComfort Ultra: </strong><a href="https://www.amazon.com/dp/B0CCZ1L489?" target="_blank" rel="nofollow"><strong>was $449 now $364</strong></a></li></ul><h3 class="article-body__section" id="section-earbud-deals"><span>Earbud Deals</span></h3><div class="product"><a data-dimension112="a9263cc0-9bfe-11f1-810b-ed37eb18eeb3" data-action="Deal Block" data-label="P20i" data-dimension48="P20i" data-dimension25="$19" href="https://www.amazon.com/dp/B0BTYCRJSS" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:800px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="gXu4CaGwCDNUjS7yXv2cP4" name="Soundcore by Anker P20i True Wireless Earbuds.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/gXu4CaGwCDNUjS7yXv2cP4.jpg" mos="" align="middle" fullscreen="" width="800" height="800" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you're willing to live without active noise-cancellation, these Soundcore by Anker P20i buds are fantastic value for money. Offering 10mm drivers for impressive "big" bass, along with Bluetooth 5.3, 30-hour battery life, water-resistance, 22 preset EQs and in-app customisation, which is great for this price.</p></div><div class="product"><a data-dimension112="a9263d24-9bfe-11f1-9f64-d3e8bbe558d1" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45" href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:103.68%;"><img id="FGXTiRGeZrNnRV5M5SvbR9" name="Beats Flex" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FGXTiRGeZrNnRV5M5SvbR9.jpg" mos="" align="middle" fullscreen="" width="679" height="704" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow" data-dimension112="a9263d24-9bfe-11f1-9f64-d3e8bbe558d1" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45">Flex Wireless: was $69 now $45</a></strong><br>While they aren't <em>technically </em>true wireless, on account of the wire that connects the buds, the Flex are a great pair of fitness earbuds. They're super comfortable thanks to their lightweight, and this $24 saving is a solid discount.<a class="view-deal button" href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow" data-dimension112="a9263d24-9bfe-11f1-9f64-d3e8bbe558d1" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45">View Deal</a></p></div><div class="product"><a data-dimension112="a9263d92-9bfe-11f1-8d11-5d0f1fe8ad68" data-action="Deal Block" data-label="AirPods 4" data-dimension48="AirPods 4" data-dimension25="$99" href="https://www.amazon.com/dp/B0DGHMNQ5Z/" 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="kTKCG8gCBjg4vScr6CMgoN" name="AirPods 4 BG image.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kTKCG8gCBjg4vScr6CMgoN.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods 4 are about as barebones as AirPods come, but they're still packed with features. Spatial Audio is on board for example, something you'd have to pay more for on the old model. 5 hours of battery isn't the very best, but they're a great on boarding place for the AirPods line.<a class="view-deal button" href="https://www.amazon.com/dp/B0DGHMNQ5Z/" target="_blank" rel="nofollow" data-dimension112="a9263d92-9bfe-11f1-8d11-5d0f1fe8ad68" data-action="Deal Block" data-label="AirPods 4" data-dimension48="AirPods 4" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="a9263e00-9bfe-11f1-8fa5-f937c009ca4d" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99" href="https://www.amazon.com/dp/B0C2W1KLSS/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:107.51%;"><img id="Nu3NHDhGaSDd5XGizimnyT" name="Studio Buds plus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Nu3NHDhGaSDd5XGizimnyT.jpg" mos="" align="middle" fullscreen="" width="679" height="730" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Studio Buds are a wonderful little pair of buds, with some big improvements over the last model. 9 hours of battery life is exemplary, extended to 36 hours with the charging case, and they're some of the most comfortable wireless in-ears around. In our <a href="https://www.tomsguide.com/reviews/beats-studio-buds-plus" data-dimension112="a9263e00-9bfe-11f1-8fa5-f937c009ca4d" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99">Beats Studio Buds Plus review</a>, we gave them 4 stars thanks to their great design and sound.<a class="view-deal button" href="https://www.amazon.com/dp/B0C2W1KLSS/" target="_blank" rel="nofollow" data-dimension112="a9263e00-9bfe-11f1-8fa5-f937c009ca4d" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="a9263e64-9bfe-11f1-98bd-1d02738061ca" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$189" href="https://www.amazon.com/Apple-Cancellation-Translation-Headphones-High-Fidelity/dp/B0FQFB8FMG" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:842px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="FYzcjQSdtuwidVacXmMNL5" name="AirPods Pro 3.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FYzcjQSdtuwidVacXmMNL5.jpg" mos="" align="middle" fullscreen="" width="842" height="842" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods Pro 3 bring the heat with 10 hours of battery life, improved sound, and superior active noise canceling. In our <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review" data-dimension112="a9263e64-9bfe-11f1-98bd-1d02738061ca" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$189">AirPods Pro 3 review</a> we said the Editor's Choice buds deliver the upgrade iPhone users have been waiting for with improved battery life, stronger noise cancellation, and smart new features like Live Translation, which delivers real-time translations to your ears. <a class="view-deal button" href="https://www.amazon.com/Apple-Cancellation-Translation-Headphones-High-Fidelity/dp/B0FQFB8FMG" target="_blank" rel="nofollow" data-dimension112="a9263e64-9bfe-11f1-98bd-1d02738061ca" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$189">View Deal</a></p></div><div class="product"><a data-dimension112="a9263ed2-9bfe-11f1-98c4-35a5eec45c15" data-action="Deal Block" data-label="Powerbeats Pro" data-dimension48="Powerbeats Pro" data-dimension25="$199" href="https://www.amazon.com/dp/B07R5QD598?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:914px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="U583dQHPqNRENp8PNfSbRU" name="Beats Powerbeats Pro_2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/U583dQHPqNRENp8PNfSbRU.jpg" mos="" align="middle" fullscreen="" width="914" height="914" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/us/powerbeats-pro,review-6461.html" data-dimension112="a9263ed2-9bfe-11f1-98c4-35a5eec45c15" data-action="Deal Block" data-label="Powerbeats Pro" data-dimension48="Powerbeats Pro" data-dimension25="$199">Powerbeats Pro</a> are a solid pair of Apple wireless fitness earbuds. They have a seamless setup, just like AirPods, and good battery life. The earbuds themselves can play music for 9 hours, and the charging case adds an additional 18 hours. If you have an iPhone and need earbuds for your workout, these are a great pick — not that they aren't great for Android, too.<a class="view-deal button" href="https://www.amazon.com/dp/B07R5QD598?" target="_blank" rel="nofollow" data-dimension112="a9263ed2-9bfe-11f1-98c4-35a5eec45c15" data-action="Deal Block" data-label="Powerbeats Pro" data-dimension48="Powerbeats Pro" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="a9263f36-9bfe-11f1-8dd6-63c847872543" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216" href="https://www.amazon.com/dp/B0CPFVB5VN?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1268px;"><p class="vanilla-image-block" style="padding-top:62.38%;"><img id="qDBpkjESq4Z3UsvHc4vEeX" name="bose open earbuds" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qDBpkjESq4Z3UsvHc4vEeX.jpg" mos="" align="middle" fullscreen="" width="1268" height="791" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CPFVB5VN?" target="_blank" rel="nofollow" data-dimension112="a9263f36-9bfe-11f1-8dd6-63c847872543" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216">Ultra Open Earbuds: was $299 now $216</a></strong><br>The Bose Ultra Open perfected the open earbud formula. They have a comfortable, non-invasive fit, solid battery life, and they sound better than you might expect from open buds. They got <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds">4 stars in our review</a>, where we loved their simple controls and surprising lack of sound leakage. <a class="view-deal button" href="https://www.amazon.com/dp/B0CPFVB5VN?" target="_blank" rel="nofollow" data-dimension112="a9263f36-9bfe-11f1-8dd6-63c847872543" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216">View Deal</a></p></div><div class="product"><a data-dimension112="a9263fa4-9bfe-11f1-828a-bbed02ddd7fb" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248" href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1509px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="zpDUbSJksxwfgT3KDaf4WS" name="Sony WF-1000XM5.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zpDUbSJksxwfgT3KDaf4WS.jpg" mos="" align="middle" fullscreen="" width="1509" height="1509" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow" data-dimension112="a9263fa4-9bfe-11f1-828a-bbed02ddd7fb" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248">WF-1000XM5: was $329 now $248</a></strong><br>These are some of the best buds that you can buy right now. They sound excellent, with some wickedly detailed sound with plenty of low-end grunt, and feature a very comfortable fit. We gave them 4.5 stars in <a href="https://www.tomsguide.com/reviews/sony-wf-1000xm5">our review</a> because of their great ANC, which manages to block out almost any noise you could throw at them. This isn't <em>quite</em> their lowest price ever, but it's very close.<a class="view-deal button" href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow" data-dimension112="a9263fa4-9bfe-11f1-828a-bbed02ddd7fb" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248">View Deal</a></p></div><h3 class="article-body__section" id="section-headphone-deals"><span>Headphone Deals</span></h3><div class="product"><a data-dimension112="a926406c-9bfe-11f1-954c-f9323b18a28b" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38" href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:510px;"><p class="vanilla-image-block" style="padding-top:154.90%;"><img id="bzH89XJXSHScmHcf8rgiyP" name="Sony_WH-C520_Headphones_cappucino_deal_block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bzH89XJXSHScmHcf8rgiyP.jpg" mos="" align="middle" fullscreen="" width="510" height="790" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Our <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-ch520-review" data-dimension112="a926406c-9bfe-11f1-954c-f9323b18a28b" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38">Sony WH-CH520 headphones review</a> said these headphones "deliver on battery life and performance."  They're 360 Reality Audio compatible for more 3D sound and you can customize EQ in the Sony Headphones app. Although there's no active noise cancelling function, they have built-in mics for hands-free voice and video calls and noise suppression processing to keep your voice sounding clear in noisy environments. They're also rated for up to 50 hours of battery life.<a class="view-deal button" href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow" data-dimension112="a926406c-9bfe-11f1-954c-f9323b18a28b" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38">View Deal</a></p></div><div class="product"><a data-dimension112="a92640da-9bfe-11f1-b818-99c01861ca49" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59" href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1190px;"><p class="vanilla-image-block" style="padding-top:113.61%;"><img id="kYA8RY5ohsqVvc9Ki7zKfL" name="JBL Tune 720BT.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kYA8RY5ohsqVvc9Ki7zKfL.jpg" mos="" align="middle" fullscreen="" width="1190" height="1352" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow" data-dimension112="a92640da-9bfe-11f1-b818-99c01861ca49" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59">Tune 720BT: was $89 now $59</a></strong><br>These budget cans pack in more features, although you'll miss out on the ANC you'll get with some of the other options on this list. Still, these headphones have a massive 76 hour battery, so they'll last you for a long, long time. Thick cushioning provides a comfortable fit for long periods of wear.<a class="view-deal button" href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow" data-dimension112="a92640da-9bfe-11f1-b818-99c01861ca49" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59">View Deal</a></p></div><div class="product"><a data-dimension112="a9264148-9bfe-11f1-8921-1ff0611ea68b" data-action="Deal Block" data-label="Solo 4" data-dimension48="Solo 4" data-dimension25="$129" href="https://www.amazon.com/dp/B0CZPHPJLN/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:717px;"><p class="vanilla-image-block" style="padding-top:154.25%;"><img id="ZDYGygzUQQsApUVGtH5RBe" name="510fGxoTsTL._AC_SL1500_.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ZDYGygzUQQsApUVGtH5RBe.jpg" mos="" align="middle" fullscreen="" width="717" height="1106" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Solo 4 might not feature noise canceling, but they make up for it by sounding pretty good. They're comfortable as well thanks to Beats new foam, and they fold up super small so that they can fit into any bag. There's also loads of battery life — 50 hours, to be exact.<a class="view-deal button" href="https://www.amazon.com/dp/B0CZPHPJLN/" target="_blank" rel="nofollow" data-dimension112="a9264148-9bfe-11f1-8921-1ff0611ea68b" data-action="Deal Block" data-label="Solo 4" data-dimension48="Solo 4" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="a92641ac-9bfe-11f1-a0af-0bbee676d453" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169" href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" 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="WbdEkaq3pnr5qszwU9U9aX" name="Beats Studio Pro on white.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WbdEkaq3pnr5qszwU9U9aX.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Studio Pro are specifically designed to bring back that thumping bass Beats is known for. They offer improved active noise cancelation and boosted battery life (up to 40 hours with ANC turned off). There's also Spatial Audio support and Beats' signature strong brand look. However, our <a href="https://www.tomsguide.com/reviews/beats-studio-pro-review" data-dimension112="a92641ac-9bfe-11f1-a0af-0bbee676d453" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169">Beats Studio Pro review</a> found the clamping force high, which may affect comfort levels for some wearers. Originally priced at $349, they've now on sale for over half off.<a class="view-deal button" href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" target="_blank" rel="nofollow" data-dimension112="a92641ac-9bfe-11f1-a0af-0bbee676d453" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169">View Deal</a></p></div><div class="product"><a data-dimension112="a926421a-9bfe-11f1-9940-1b87e9849042" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248" href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B09XS7JWHH?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1381px;"><p class="vanilla-image-block" style="padding-top:108.62%;"><img id="TWLQcjD9oitTFCdEHsgTe5" name="Sony WH-1000XM5 deal block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TWLQcjD9oitTFCdEHsgTe5.jpg" mos="" align="middle" fullscreen="" width="1381" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B09XS7JWHH?" target="_blank" rel="nofollow" data-dimension112="a926421a-9bfe-11f1-9940-1b87e9849042" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248">WH-1000XM5: was $399 now $248</a></strong><br>The XM5s are Sony's premium noise-canceling headphones and our pick for the best headphones on the market. The large, over-ear cushions are incredibly comfortable and the active noise cancelation is excellent. Plus, despite ANC being a drain on battery life, these amazing headphones still give you up to 30 hours with ANC enabled. You can push that up to 40 hours of battery life with ANC turned off.<a class="view-deal button" href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B09XS7JWHH?" target="_blank" rel="nofollow" data-dimension112="a926421a-9bfe-11f1-9940-1b87e9849042" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248">View Deal</a></p></div><div class="product"><a data-dimension112="a926427e-9bfe-11f1-8f87-b57fbf353032" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$364" href="https://www.amazon.com/dp/B0CCZ1L489?" 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="g64SDV3bmD2tcgopiiNgP6" name="Bose QuietComfort Ultra headphones Versus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/g64SDV3bmD2tcgopiiNgP6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Headphones get you epic ANC, super sound, and the most comfortable fit this side of wrapping your head in a snood that plays music. 24 hours of battery life isn't the best, but they make up for it with just about everything else.<a class="view-deal button" href="https://www.amazon.com/dp/B0CCZ1L489?" target="_blank" rel="nofollow" data-dimension112="a926427e-9bfe-11f1-8f87-b57fbf353032" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$364">View Deal</a></p></div><div class="product"><a data-dimension112="a92642ec-9bfe-11f1-ab86-5d4657ada425" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$429" href="https://www.amazon.com/dp/B0GSS4SGZR?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1356px;"><p class="vanilla-image-block" style="padding-top:110.62%;"><img id="augarq3rWrbuXENFrmUU88" name="airpods max 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/augarq3rWrbuXENFrmUU88.jpg" mos="" align="middle" fullscreen="" width="1356" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods Max 2 are Apple's latest flagship over-ears, with hi-res audio playback via USB-C and Bluetooth, seamless Siri integration and smart features like Live Translation, intuitive controls, and a sleek, iconic design.<a class="view-deal button" href="https://www.amazon.com/dp/B0GSS4SGZR?" target="_blank" rel="nofollow" data-dimension112="a92642ec-9bfe-11f1-ab86-5d4657ada425" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$429">View Deal</a></p></div><h3 class="article-body__section" id="section-shop-more-deals"><span>Shop More Deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Headphones&min_discount_ratio=0.95&offer_type=all&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("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/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[ AirPods Ultra with cameras just leaked in video — and the privacy debate has already started ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Apple's AirPods are a mainstay on our list of <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> due to things like sound quality, noise canceling, and the way they fit seamlessly into the Apple ecosystem. While the AirPods are almost universally loved, the next iteration of the famous white buds could prove a bit more divisive.</p><p>We've heard rumors for awhile that a new pair of AirPods reportedly called the <a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know">'AirPods Ultra' could feature on-board cameras</a> for the first time. These are believed to act as a visual input for Siri and could be revealed next month at Apple's iPhone 18 event.</p><p>Now, the team at <a href="https://www.macrumors.com/2026/08/17/camera-equipped-airpods-macos-26-7/" target="_blank">MacRumors</a> claims to have uncovered a video in the macOS Tahoe 26.7 release candidate showing the AirPods Ultra in action.</p><p>The video shows a man picking up a book and glancing at it while wearing a pair of distinctive white AirPods. Meanwhile, a Siri-esque voiceover says, "With Visual Intelligence, your world becomes savable. See something you like? Just ask me to save it for later."</p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2089522745184760112"><p lang="en" dir="ltr">Here is your first look at AirPods with Cameras in action using Visual Intelligence.This video file came from macOS 26.7 RC pic.twitter.com/yo7RI4MCeu<a href="https://twitter.com/cantworkitout/status/2089522745184760112">August 18, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>According to the site, if an obstruction like long hair is in the way of the AirPod cameras, the wearer receives an alert that reportedly states: "To get the most accurate information about things in your environment, make sure AirPods are not covered."</p><p><strong>Unlike some of the best smart glasses, the cameras on the AirPods Ultra aren't designed for taking pictures. Instead, they're believed to act as a way to</strong><a href="https://www.tomsguide.com/ai/apple-intelligence/ive-been-testing-siri-ai-and-these-3-features-prove-its-actually-smart-now"><strong> feed Siri information for Visual Intelligence</strong></a> without users having to take out their iPhone to snap a picture. A small LED is expected to be included that will reveal when the earbuds are capturing information from the outside world.</p><h2 id="avoiding-a-backlash">Avoiding a backlash</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:59.79%;"><img id="x3QYkWuVwcfhMy8FaWP38H" name="AirPods backlash" alt="Tweets related to cameras on AirPods" src="https://cdn.mos.cms.futurecdn.net/x3QYkWuVwcfhMy8FaWP38H.jpg" mos="" align="middle" fullscreen="" width="1920" height="1148" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide/X.com)</span></figcaption></figure><p>Launching any kind of head-mounted wearable with cameras attached is turning into a risky endeavor right now. There's a growing<a href="https://www.tomsguide.com/computing/smart-glasses/im-worried-smart-glasses-might-ruin-wearable-displays-and-of-course-its-the-cameras"> privacy debate about the 'pervert glasses' aspect of smart eyewear</a> and Apple could easily fall into this trap if it doesn't nail the messaging.</p><p>A quick glance over at X shows plenty of users ready to be concerned about what Apple has planned. </p><p>The user <a href="https://x.com/MatheasRomak/status/2089643311019761971">@MatheasRomak wrote on X</a>: "Yuck… I don’t need the NSA stapled to my head."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="K94scvpChC7FjQZnjYwzN" name="AirPods backlash 2" alt="AirPods with cameras discussion on social media" src="https://cdn.mos.cms.futurecdn.net/K94scvpChC7FjQZnjYwzN.jpg" mos="" align="middle" fullscreen="" width="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 / X.com)</span></figcaption></figure><p>But an equal number seem convinced that Apple will integrate this tech in a non-intrusive way. </p><p>For example, the <a href="https://x.com/imlwi/status/2089655125073686537">X user @imlwi writes</a>: "We're seeing Meta AI Glasses get banned due to privacy.   I'm guessing these AirPods will temporarily capture a few frames, identify the object, send Siri something like the book/author, throw away the camera frames."</p><p>I don't imagine Apple will face the same kind of backlash that's currently <a href="https://www.tomsguide.com/ai/meta-files-patent-for-ai-camera-glasses-that-use-facial-recognition-expression-analysis-and-information-about-your-relationships-to-identify-people-around-you-and-create-highlights">plaguing Meta</a>, especially if it sticks to privacy-centric messaging. </p><p>Furthermore, the AirPods are one of Apple's best-selling and most popular products with a dedicated install base. Apple goes to great lengths to process data on-device and if the company can demonstrate that the AirPods Ultra can meaningfully improve Siri's capabilities without compromising privacy, I think adoption will overcome the criticism.</p><h2 id="genuinely-useful">Genuinely useful</h2><p>Apple's innovations with the AirPods have already <a href="https://www.tomsguide.com/audio/airpods-pro-2-hearing-health-features-are-now-available-in-latest-firmware">helped those who are hard of hearing,</a> and giving the AirPods cameras could be a game-changer. One of the rumors I'm particularly excited about is that the AirPods Ultra will be able to use external landmarks to help you navigate through unfamiliar areas. Voice prompts will tie to what the earbuds see and guide you around a foreign city.</p><p>When you combine this with the buds' real-time translation feature, you arrive at a genuinely useful feature for anyone who travels regularly.</p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2089578669803176094"><p lang="en" dir="ltr">I have long said (albeit with no audience) that AI devices like the Humane pin and others in that form factor would not succeed because the camera was not following our gaze. When I walk down the street and see something in a shop window I don’t turn my whole body to face it.…<a href="https://twitter.com/cantworkitout/status/2089578669803176094">August 18, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>Let me know in the comments below if you find the idea of camera-equipped AirPods concerning. In the meantime, here's <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-18-event-all-the-new-products-expected-to-launch-in-september">everything we expect to see at Apple's iPhone 18 event next month</a>.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZQjKO"></div>                            </div>                            <script src="https://kwizly.com/embed/XZQjKO.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/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know">AirPods Ultra with cameras are 'nearly ready' — here's 4 features you need to know</a></li><li><a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-almost-certainly-hinted-at-in-ios-27-code-and-they-could-be-a-huge-audio-and-camera-upgrade">AirPods Ultra 'almost certainly' hinted at in iOS 27 code — and they could be a huge audio (and camera) upgrade</a></li><li><a href="https://www.tomsguide.com/phones/iphones/apple-iphone-18-event-all-the-new-products-expected-to-launch-in-september">Apple iPhone 18 event — all the new products expected to launch in September</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/airpods/airpods-ultra-with-cameras-just-appeared-in-leaked-video-and-the-privacy-debate-has-already-started</link>
                                                                            <description>
                            <![CDATA[ A leaked video may have confirmed the imminent arrival of the next generation of AirPods. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">TYb2mHpHWHjxgNRNzF3ynR</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/k9ErLVhkHsuycKsa3Xzej8-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 18 Aug 2026 11:27:44 +0000</pubDate>                                                                                                                                <updated>Tue, 18 Aug 2026 12:02:58 +0000</updated>
                                                                                                                                            <category><![CDATA[Airpods]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ jeff.parsons@futurenet.com (Jeff Parsons) ]]></author>                    <dc:creator><![CDATA[ Jeff Parsons ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/7z3UTGGrmSokMKxTWHmhjX.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Jeff is U.K. Editor-in-Chief for Tom’s Guide looking after the day-to-day output of the site’s British contingent. &lt;/p&gt;&lt;p&gt;Rising early and heading straight for the coffee machine, Jeff loves nothing more than dialling into the zeitgeist of the day’s tech news. A journalist for over a decade, he&#039;s travelled around the world testing and reviewing any gadget he can get his hands on.&lt;/p&gt;&lt;p&gt;Before joining the team at Tom’s Guide, Jeff covered technology and science for two of the U.K.’s biggest national news sites: Metro.co.uk and the Daily Mirror. Memorable moments include getting lost in Vienna in an electric Audi, touring Lockheed Martin’s mile-long jet factory in Fort Worth and filming a Netflix documentary about Elon Musk in West London.&lt;/p&gt;&lt;p&gt;When not plugged into the current news agenda, editing or commissioning a series of articles or debating the merits of Apple vs Android, Jeff can usually be found out for a run trying to shave precious seconds off his PB. Or lifting weights in a vain attempt to offset the ageing process.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/k9ErLVhkHsuycKsa3Xzej8-1280-80.jpg">
                                                            <media:credit><![CDATA[Regan Coule/Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[AirPods Pro]]></media:description>                                                            <media:text><![CDATA[AirPods Pro]]></media:text>
                                <media:title type="plain"><![CDATA[AirPods Pro]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/k9ErLVhkHsuycKsa3Xzej8-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Apple's AirPods are a mainstay on our list of <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> due to things like sound quality, noise canceling, and the way they fit seamlessly into the Apple ecosystem. While the AirPods are almost universally loved, the next iteration of the famous white buds could prove a bit more divisive.</p><p>We've heard rumors for awhile that a new pair of AirPods reportedly called the <a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know">'AirPods Ultra' could feature on-board cameras</a> for the first time. These are believed to act as a visual input for Siri and could be revealed next month at Apple's iPhone 18 event.</p><p>Now, the team at <a href="https://www.macrumors.com/2026/08/17/camera-equipped-airpods-macos-26-7/" target="_blank">MacRumors</a> claims to have uncovered a video in the macOS Tahoe 26.7 release candidate showing the AirPods Ultra in action.</p><p>The video shows a man picking up a book and glancing at it while wearing a pair of distinctive white AirPods. Meanwhile, a Siri-esque voiceover says, "With Visual Intelligence, your world becomes savable. See something you like? Just ask me to save it for later."</p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2089522745184760112"><p lang="en" dir="ltr">Here is your first look at AirPods with Cameras in action using Visual Intelligence.This video file came from macOS 26.7 RC pic.twitter.com/yo7RI4MCeu<a href="https://twitter.com/cantworkitout/status/2089522745184760112">August 18, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>According to the site, if an obstruction like long hair is in the way of the AirPod cameras, the wearer receives an alert that reportedly states: "To get the most accurate information about things in your environment, make sure AirPods are not covered."</p><p><strong>Unlike some of the best smart glasses, the cameras on the AirPods Ultra aren't designed for taking pictures. Instead, they're believed to act as a way to</strong><a href="https://www.tomsguide.com/ai/apple-intelligence/ive-been-testing-siri-ai-and-these-3-features-prove-its-actually-smart-now"><strong> feed Siri information for Visual Intelligence</strong></a> without users having to take out their iPhone to snap a picture. A small LED is expected to be included that will reveal when the earbuds are capturing information from the outside world.</p><h2 id="avoiding-a-backlash">Avoiding a backlash</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:59.79%;"><img id="x3QYkWuVwcfhMy8FaWP38H" name="AirPods backlash" alt="Tweets related to cameras on AirPods" src="https://cdn.mos.cms.futurecdn.net/x3QYkWuVwcfhMy8FaWP38H.jpg" mos="" align="middle" fullscreen="" width="1920" height="1148" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide/X.com)</span></figcaption></figure><p>Launching any kind of head-mounted wearable with cameras attached is turning into a risky endeavor right now. There's a growing<a href="https://www.tomsguide.com/computing/smart-glasses/im-worried-smart-glasses-might-ruin-wearable-displays-and-of-course-its-the-cameras"> privacy debate about the 'pervert glasses' aspect of smart eyewear</a> and Apple could easily fall into this trap if it doesn't nail the messaging.</p><p>A quick glance over at X shows plenty of users ready to be concerned about what Apple has planned. </p><p>The user <a href="https://x.com/MatheasRomak/status/2089643311019761971">@MatheasRomak wrote on X</a>: "Yuck… I don’t need the NSA stapled to my head."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="K94scvpChC7FjQZnjYwzN" name="AirPods backlash 2" alt="AirPods with cameras discussion on social media" src="https://cdn.mos.cms.futurecdn.net/K94scvpChC7FjQZnjYwzN.jpg" mos="" align="middle" fullscreen="" width="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 / X.com)</span></figcaption></figure><p>But an equal number seem convinced that Apple will integrate this tech in a non-intrusive way. </p><p>For example, the <a href="https://x.com/imlwi/status/2089655125073686537">X user @imlwi writes</a>: "We're seeing Meta AI Glasses get banned due to privacy.   I'm guessing these AirPods will temporarily capture a few frames, identify the object, send Siri something like the book/author, throw away the camera frames."</p><p>I don't imagine Apple will face the same kind of backlash that's currently <a href="https://www.tomsguide.com/ai/meta-files-patent-for-ai-camera-glasses-that-use-facial-recognition-expression-analysis-and-information-about-your-relationships-to-identify-people-around-you-and-create-highlights">plaguing Meta</a>, especially if it sticks to privacy-centric messaging. </p><p>Furthermore, the AirPods are one of Apple's best-selling and most popular products with a dedicated install base. Apple goes to great lengths to process data on-device and if the company can demonstrate that the AirPods Ultra can meaningfully improve Siri's capabilities without compromising privacy, I think adoption will overcome the criticism.</p><h2 id="genuinely-useful">Genuinely useful</h2><p>Apple's innovations with the AirPods have already <a href="https://www.tomsguide.com/audio/airpods-pro-2-hearing-health-features-are-now-available-in-latest-firmware">helped those who are hard of hearing,</a> and giving the AirPods cameras could be a game-changer. One of the rumors I'm particularly excited about is that the AirPods Ultra will be able to use external landmarks to help you navigate through unfamiliar areas. Voice prompts will tie to what the earbuds see and guide you around a foreign city.</p><p>When you combine this with the buds' real-time translation feature, you arrive at a genuinely useful feature for anyone who travels regularly.</p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2089578669803176094"><p lang="en" dir="ltr">I have long said (albeit with no audience) that AI devices like the Humane pin and others in that form factor would not succeed because the camera was not following our gaze. When I walk down the street and see something in a shop window I don’t turn my whole body to face it.…<a href="https://twitter.com/cantworkitout/status/2089578669803176094">August 18, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>Let me know in the comments below if you find the idea of camera-equipped AirPods concerning. In the meantime, here's <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-18-event-all-the-new-products-expected-to-launch-in-september">everything we expect to see at Apple's iPhone 18 event next month</a>.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XZQjKO"></div>                            </div>                            <script src="https://kwizly.com/embed/XZQjKO.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/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know">AirPods Ultra with cameras are 'nearly ready' — here's 4 features you need to know</a></li><li><a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-almost-certainly-hinted-at-in-ios-27-code-and-they-could-be-a-huge-audio-and-camera-upgrade">AirPods Ultra 'almost certainly' hinted at in iOS 27 code — and they could be a huge audio (and camera) upgrade</a></li><li><a href="https://www.tomsguide.com/phones/iphones/apple-iphone-18-event-all-the-new-products-expected-to-launch-in-september">Apple iPhone 18 event — all the new products expected to launch in September</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Apple iPhone 18 event — all the new products expected to launch at the 'Surprise and shine' event ]]></title>
                                                                                                <dc:content><![CDATA[ <div  class="fancy-box"><div class="fancy_box-title">Apple iPhone 18 event latest news and rumors</div><div class="fancy_box_body"><p class="fancy-box__body-text"><em><strong>Updated September 1</strong></em></p><p class="fancy-box__body-text">• <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-18-pro-max-unboxed-ahead-of-apple-event-these-could-be-the-final-designs">Unboxing video</a> gives us our best look at iPhone 18 Pro Max and Ultra<br>• Pre-order date may be delayed to <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-iphone-18-pro-pre-order-date-just-tipped-heres-when-you-can-get-yours">Saturday, September 12</a><br>• <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/apple-event-is-official-for-september-9-iphone-ultra-iphone-18-pro-max-watch-12-and-more">"Surprise and shine"</a> event officially set for Wednesday, September 9</p></div></div><p>Apple's annual iPhone event has been <a href="https://www.tomsguide.com/phones/iphones/apple-event-is-official-for-september-9-iphone-ultra-iphone-18-pro-max-watch-12-and-more">officially confirmed to take place on September 9</a>, and it could easily be the biggest Apple event in years. We're expecting a slew of new devices to be unveiled as John Ternus takes the reins as Apple's new CEO. </p><p>And all of that is before we discuss the launch of the long-rumored, much-hyped foldable <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-rumors-everything-you-need-to-know-about-the-foldable-iphone">iPhone Ultra</a> — Apple's first attempt at a <a href="https://www.tomsguide.com/best-picks/best-foldable-phones">foldable phone</a>. It's not clear when the device will actually launch, but at the very least, its reveal is almost certainly right around the corner. </p><p>In all, Apple is expected to launch up to eight different devices, from the expected new iPhones and upgraded Apple Watches to several new smart home-focused products. </p><p>Here's what we expect to see at <a href="https://www.tomsguide.com/phones/iphones/apple-event-is-official-for-september-9-iphone-ultra-iphone-18-pro-max-watch-12-and-more?hasComeFromProof=true">Apple's 'Surprise and shine' September event</a>, and when it's kicking off</p><h2 id="when-is-the-iphone-18-event">When is the iPhone 18 event?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2066px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="fnjPCS2zVb2TZ4GCnuqwK5" name="Apple suprise and shine" alt="Apple fall event 2026." src="https://cdn.mos.cms.futurecdn.net/fnjPCS2zVb2TZ4GCnuqwK5.jpg" mos="" align="middle" fullscreen="" width="2066" height="1162" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>Apple has <strong>officially confirmed its "Surprise and shine" event will be held on Wednesday, September 9 at Apple Park</strong>. Although Apple normally holds its events on a Tuesday, the Labor Day weekend means it's being moved forward to Wednesday.</p><p>This has happened in the past, and the reasoning is that Apple doesn't want to ask attendees to travel on a holiday weekend. For example, the <a href="https://www.tomsguide.com/reviews/iphone-14">iPhone 14</a> launched on Wednesday, September 7, following Labor Day on Monday 5, and that'll likely be the case this year too.</p><p>The <strong>event is set to take place at 10am PT / 1pm ET / 6pm BST</strong> and will livestreamed on various social media platforms as well as Apple's own site.</p><p>iPhones usually go up for pre-order the Friday following the Apple event, but this year that's September 11. Bloomberg's Mark Gurman has reported that pre-orders will be <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-iphone-18-pro-pre-order-date-just-tipped-heres-when-you-can-get-yours">pushed to Saturday, September 12</a>.</p><p>This year's edition will be hosted by incoming CEO John Ternus, and it looks like his tenure will start with a bang.</p><h2 class="article-body__section" id="section-new-iphones"><span>New iPhones</span></h2><h3 id="iphone-18-pro-and-pro-max">iPhone 18 Pro and Pro Max</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UHNuNim9UYH2mmGNTn3S4V" name="Apple-iPhone 18 Pro-A20-Pro_LIST" alt="Apple iPhone 18 Pro and the A20 Pro chip" src="https://cdn.mos.cms.futurecdn.net/UHNuNim9UYH2mmGNTn3S4V.jpg" mos="" align="middle" fullscreen="" width="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/Apple)</span></figcaption></figure><p>This year, Apple is expected to launch only its <a href="https://www.tomsguide.com/phones/iphones/iphone-18-rumors-everything-we-know-so-far">iPhone 18 Pro</a> and <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-max-rumors-and-leaks-everything-we-know-so-far">iPhone 18 Pro Max</a> models. The entry-level iPhone 18 and iPhone 18 Plus reportedly <a href="https://www.tomsguide.com/phones/iphones/iphone-18-release-schedule-could-be-changing-but-this-rumor-makes-no-sense">won't debut until next spring</a> alongside the iPhone 18e model.</p><p>The iPhone 18 Pro and iPhone 18 Pro Max are both tipped to feature Apple's new 2nm A20 Pro chip, a smaller Dynamic Island and Apple's new C2 modem. And both phones should get larger batteries.</p><p>However, the iPhone 18 Pro Max might have some exclusive upgrades, including a variable aperture camera and longer zoom. For more see our <a href="https://www.tomsguide.com/phones/iphones/3-big-reasons-to-buy-iphone-18-pro-max-over-the-iphone-18-pro-this-year">iPhone 18 Pro vs Pro Max</a> comparison.</p><p>Unforutnately, <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-could-see-absolutely-massive-price-hike-according-to-researchers">analysts have predicted</a> that the new iPhone 18 Pros could cost between $200 and $350 higher than the iPhone 17 Pro family. </p><h3 id="iphone-ultra">iPhone Ultra</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aUJZxCfMMXC3m2nKLVB2dV" name="iPhone Fold final design 1" alt="iPhone Fold design" src="https://cdn.mos.cms.futurecdn.net/aUJZxCfMMXC3m2nKLVB2dV.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Majin Bu on X)</span></figcaption></figure><p>The biggest announcement of the Apple event is sure to be the debut of the foldable iPhone Ultra.</p><p>Leaks have suggested that it will have more of a wider shape, similar to the <a href="https://www.tomsguide.com/phones/samsung-phones/samsung-galaxy-z-fold-8-review-wide-for-the-win">Galaxy Z Fold 8</a>, with a <a href="https://www.tomsguide.com/phones/iphones/iphone-fold-specs-potentially-revealed-heres-how-it-would-stack-up-against-samsung-galaxy-z-fold-8">7.8-inch foldable display and a 5.5-inch cover screen</a>. It could measure between 4.5 and 4.8mm when unfolded, making it one of the thinnest iPhones ever.</p><p>It's rumored to sport a 4,883 mAh battery, the A20 Pro chipset, Apple's in-house C2 modem, and a Touch ID fingerprint scanner in the power button. The rear is expected to come with a pair of 48MP cameras, horizontally positioned like the back of the iPhone Air, alongside two separate 24MP selfie cameras — one for each screen.</p><p>Apple reportedly delayed releasing a folding iPhone until it could eliminate the crease in the display, but whether it's accomplished this remains to be seen. Even Samsung and Oppo, despite their long lead time, have only <a href="https://www.tomsguide.com/phones/samsung-phones/comparing-galaxy-z-fold-8-vs-oppo-find-n6-crease">had moderate success</a>.</p><p>One downside is that the iPhone Ultra could be very expensive, costing anywhere from <a href="https://www.tomsguide.com/phones/iphones/usd1-299-iphone-18-pro-max-analyst-now-predicts-usd200-price-hike-for-apples-next-flagship-phone">$2,000 to 2,500</a>. And a recent leak suggests that<a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-leakers-reveal-new-details-around-release-and-extremely-limited-availability"> iPhone Ultra may not be immediately available, </a>with only "extremely limited" availability.</p><p>Just keep in mind that the <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-trade-offs-the-biggest-things-youre-not-getting-versus-iphone-18-pro">iPhone Ultra could have a number of trade-offs</a> versus the iPhone 18 Pro, including the lack of a telephoto zoom.</p><h2 class="article-body__section" id="section-airpods-ultra"><span>AirPods Ultra</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:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kXLZ6v6RAcuyoeV3PftvHP" name="AirPods Pro" alt="AirPods Pro" src="https://cdn.mos.cms.futurecdn.net/kXLZ6v6RAcuyoeV3PftvHP.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Regan Coule/Tom's Guide)</span></figcaption></figure><p>These ones are a little surprising as based on a <a href="https://www.tomsguide.com/audio/airpods/gurman-ai-airpods-with-cameras-just-tipped-for-2027-launch-in-flurry-of-new-releases">previous Gurman report</a>, we weren't expecting the AirPods Ultra until 2027 at the earliest. However,<a href="https://www.youtube.com/watch?v=wbBy1KXj5aM" target="_blank"> rumor aggregator AppleTrack</a> just reported this week that the Ultra version of AirPods will launch at the iPhone event. </p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2089522745184760112"><p lang="en" dir="ltr">Here is your first look at AirPods with Cameras in action using Visual Intelligence.This video file came from macOS 26.7 RC pic.twitter.com/yo7RI4MCeu<a href="https://twitter.com/cantworkitout/status/2089522745184760112">August 18, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>The big change with these headphones is they will allegedly<a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know"> feature a camera</a> that will feed into Siri to allow it to "see" the world. </p><p>It would utilize a version of Visual Intelligence to let you ask Siri questions about your surroundings or get turn-by-turn directions from the more <a href="https://www.tomsguide.com/phones/iphones/i-spent-a-week-with-siri-ai-here-are-my-honest-pros-and-cons">powerful AI Siri </a>releasing this fall.</p><p>The <a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-with-cameras-just-appeared-in-leaked-video-and-the-privacy-debate-has-already-started?hasComeFromProof=true">AirPods Ultra has also just appeared in a leaked video</a> buried within the macOS Tahoe 26.7 release candidate, and it shows how they'll help you identify objects in the real world. </p><h2 class="article-body__section" id="section-apple-watch-12-and-apple-watch-ultra-4"><span>Apple Watch 12 and Apple Watch Ultra 4</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LPjRVZraPvxRyivUNsMQaG" name="Apple Watch Series 11" alt="Apple Watch Series 11" src="https://cdn.mos.cms.futurecdn.net/LPjRVZraPvxRyivUNsMQaG.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>Unlike the iPhones, we know a bit less about the next Apple Watches. We are expecting two new models in the <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-series-12-everything-we-know-so-far">Apple Watch Series 12</a> and the <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-ultra-4-these-are-the-upgrades-apple-needs-to-make-to-remain-the-smartwatch-king">Apple Watch Ultra 4</a>.</p><p>The leaks have been sparse, but Apple is reportedly not bringing many upgrades this year. The biggest change might be a <a href="https://www.tomsguide.com/wellness/smartwatches/leaked-apple-watch-12-design-change-is-the-opposite-of-an-upgrade-and-that-has-me-worried">new kind of band attachment</a>. This does mean that your old bands might not be compatible with the new watches.</p><p>Reportedly, Apple could <a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">offer a ceramic case option</a> for the first time in years. It should also sport new health and fitness-related upgrades. We already know that <a href="https://www.tomsguide.com/wellness/smartwatches/watchos-27-all-the-new-features-coming-to-apple-watch-later-this-year#section-watchos-27-new-health-and-fitness-features">watchOS 27</a> will bring new AI features, an updated Workout Buddy and more useful data tracking. Plus, it should feature better integration with the upgraded Siri AI which we've already seen in the <a href="https://www.tomsguide.com/phones/iphones/ios-27-public-beta-is-now-live-9-features-you-need-to-try-first">iOS 27 beta</a>.</p><h2 class="article-body__section" id="section-smart-home"><span>Smart Home</span></h2><p>Apple's smart home ambitions and the rumors surrounding them need to be taken with a massive grain of salt. Leaks have suggested Apple is planning on introducing a suite of smart home devices starting with a revamped HomePod and an AI-powered Apple TV 4K streaming device. </p><p>Those rumors have existed for more than two years and largely feed into the company's promises around the AI-powered Siri.</p><p>However, this year with iOS 27, Apple may finally have the chatbot that was promised. If it actually releases and works as pitched, this year could actually be the start of Apple Home. </p><h3 id="homepod-home-hub">HomePod Home hub</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Maj5caC4eG4rJYyquwrf3L" name="HomePod with screen edit" alt="HomePod with display concept render" src="https://cdn.mos.cms.futurecdn.net/Maj5caC4eG4rJYyquwrf3L.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Yanko Design)</span></figcaption></figure><p>Bloomberg's Mark Gurman, long a reporter of the rumored HomePod updates, <a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">believes </a>that a new <a href="https://www.tomsguide.com/reviews/apple-homepod-mini">HomePod mini</a> could launch this September alongside the rumored HomePod Home hub.</p><p>Several different iterations of this device have been rumored, including one with a <a href="https://www.tomsguide.com/home/smart-home/apples-next-big-thing-could-be-a-dollar1000-home-robot-ipad-combo">$1,000 robot arm</a>. The most common version involves a <a href="https://www.tomsguide.com/home/smart-home/apple-tipped-to-launch-ai-wall-tablet-to-control-your-home-in-march-2025-everything-we-know-so-far">wall-mounted touchpad</a> or a HomePod speaker with a touch display about the size of an iPad mini. </p><p>This device would have a FaceTime camera for video calls and presence sensors to initiate smart home routines. </p><h3 id="homepod-mini-2-2">HomePod mini 2</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qzn6VBsNWsuQVgzKVVPvhA" name="Apple HomePod mini midnight.jpg" alt="Apple HomePod mini in midnight color" src="https://cdn.mos.cms.futurecdn.net/qzn6VBsNWsuQVgzKVVPvhA.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>A  <a href="https://www.tomsguide.com/home/smart-home/new-apple-tv-and-homepod-mini-reportedly-launching-in-2025-with-apple-networking-chip">new HomePod mini</a> might also debut, but it might not be as big of an upgrade as the Hub. Most rumors suggest that it will feature a new chip and audio improvements.</p><p>To be clear, this one feels even more far-fetched than the Hub; the HomePod mini 2 has been on our <a href="https://www.tomsguide.com/news/hoping-for-a-homepod-mini-2-to-go-with-the-new-homepod-dont-hold-your-breath">rumor radar since at least 2023</a>. The current version debuted in 2020, and like the next device, is long overdue for an update.</p><h3 id="apple-tv-4k-2026">Apple TV 4K 2026</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3840px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="ueDcVd52GdTw3wgnvCuAkU" name="Google TV Streamer vs Apple TV 4K-3" alt="Google TV Streamer vs Apple TV 4K" src="https://cdn.mos.cms.futurecdn.net/ueDcVd52GdTw3wgnvCuAkU.jpg" mos="" align="middle" fullscreen="" width="3840" height="2156" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Like the HomePod series, Apple hasn't released a new version of the <a href="https://www.tomsguide.com/reviews/apple-tv-4k-2022">Apple TV 4K</a> since 2022. We're long overdue for an upgrade — even if it's only a more powerful chip for stronger performance. </p><p>Previously, it was suggested that a new Apple TV could feature <a href="https://www.tomsguide.com/entertainment/streaming-devices/new-apple-tv-4k-biggest-upgrades-just-leaked-ahead-of-launch">the A17 Pro chip</a>, but it's more likely it'll get the new A20 or, to save some money, last year's A19. The device should also get the <a href="https://www.tomsguide.com/phones/iphones/new-iphone-18-pro-chips-camera-upgrades-and-more-leaked-by-analyst-heres-what-we-know">rumored N2 chip </a>for better Wi-Fi and Bluetooth connectivity. </p><p>Outside of chips, the big upgrade would be Siri AI, which could let you connect the Apple TV as a hub for smart devices. One rumor suggested that Apple might release a <a href="https://www.bloomberg.com/news/newsletters/2024-04-07/apple-aapl-future-products-could-include-robots-and-smart-home-push-lupinmnahttps:/www.bloomberg.com/news/newsletters/2024-04-07/apple-aapl-future-products-could-include-robots-and-smart-home-push-lupinmna" target="_blank">set-top box with a camera,</a> which would allow for FaceTime calls and gesture-based controls.</p><p>Will you be tuning in to Apple's event next month? Let me know which device you're most hyped for in the comments.</p><h2 class="article-body__section" id="section-what-we-don-t-expect-to-see"><span>What we don't expect to see</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aRTWJedXhSdn4zKRv2Rn9e" name="Apple iPhone 17 review" alt="Apple iPhone 17 review." src="https://cdn.mos.cms.futurecdn.net/aRTWJedXhSdn4zKRv2Rn9e.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: Tom's Guide / John Velasco)</span></figcaption></figure><p>Even with a huge event like this, there are still going to be devices that <a href="https://www.tomsguide.com/phones/iphones/apples-iphone-18-pro-launch-event-is-coming-but-dont-expect-these-5-products-to-appear">won't make an appearance</a>. Due to staggered release schedules and supply-chain timing, Apple is spreading its hardware roadmap across multiple release windows over the next year. As a result, the following devices are unlikely to make any sort of appearance at the event.</p><ul><li><strong>iPhone 18:</strong> Apple is reportedly holding back the base model until early 2027 to drive early demand toward the Pro models.</li><li><strong>AirPods Ultra:</strong> Rumored AirPods with cameras for Siri AI context are reportedly delayed until 2027 due to software and supply-chain bottlenecks.</li><li><strong>New Macs:</strong> Upgraded M6 MacBooks and touchscreen models are typically saved for dedicated computing announcements in October or later.</li><li><strong>OLED iPad mini:</strong> The next refresh of the compact tablet is slated for an October arrival rather than competing for stage time with the new iPhones.</li><li><strong>Apple Watch SE (4th gen):</strong> The entry-level wearable update will sit out this event, with Apple likely to focus on the Series 12 and Ultra 4 instead.</li></ul><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/phones/iphones/iphone-ultra-leakers-reveal-new-details-around-release-and-extremely-limited-availability">iPhone Ultra leakers reveal new details around release and 'extremely limited' availability</a></li><li><a href="https://www.tomsguide.com/phones/the-5-accessories-that-transformed-my-iphone-17-pro-max-for-under-usd50">The 5 accessories that transformed my iPhone 17 Pro Max for under $50</a></li><li><a href="https://www.tomsguide.com/news/ultimate-back-to-school-guide">Ultimate back to school guide 2026: Laptops, Chromebooks, headphones and more</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/phones/iphones/apple-iphone-18-event-all-the-new-products-expected-to-launch-in-september</link>
                                                                            <description>
                            <![CDATA[ Apple could have at least eight new products to announce on September 9, from iPhone and watches to new smart home devices. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">MjZS5SyhQTwebwf4QfXhvK</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/XK7W9gHWEgHPdh5kir8n9-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 18 Aug 2026 09:03:20 +0000</pubDate>                                                                                                                                <updated>Tue, 08 Sep 2026 09:15:33 +0000</updated>
                                                                                                                                            <category><![CDATA[iPhones]]></category>
                                                    <category><![CDATA[Smartwatches]]></category>
                                                    <category><![CDATA[Smart Home]]></category>
                                                    <category><![CDATA[Apple TV]]></category>
                                                    <category><![CDATA[Airpods]]></category>
                                                    <category><![CDATA[Phones]]></category>
                                                    <category><![CDATA[Wellness]]></category>
                                                    <category><![CDATA[Home]]></category>
                                                    <category><![CDATA[Entertainment]]></category>
                                                    <category><![CDATA[Streaming]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></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.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;br&gt;Send secure tips on Signal: scyo1.55.&lt;/p&gt; ]]></dc:description>
                                                                                                        <dc:contributor><![CDATA[ Tom Pritchard ]]></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/XK7W9gHWEgHPdh5kir8n9-1280-80.jpg">
                                                            <media:credit><![CDATA[Apple]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[John Ternus and Tim Cook]]></media:description>                                                            <media:text><![CDATA[John Ternus and Tim Cook]]></media:text>
                                <media:title type="plain"><![CDATA[John Ternus and Tim Cook]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/XK7W9gHWEgHPdh5kir8n9-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <div  class="fancy-box"><div class="fancy_box-title">Apple iPhone 18 event latest news and rumors</div><div class="fancy_box_body"><p class="fancy-box__body-text"><em><strong>Updated September 1</strong></em></p><p class="fancy-box__body-text">• <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-18-pro-max-unboxed-ahead-of-apple-event-these-could-be-the-final-designs">Unboxing video</a> gives us our best look at iPhone 18 Pro Max and Ultra<br>• Pre-order date may be delayed to <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-iphone-18-pro-pre-order-date-just-tipped-heres-when-you-can-get-yours">Saturday, September 12</a><br>• <a data-analytics-id="inline-link" href="https://www.tomsguide.com/phones/iphones/apple-event-is-official-for-september-9-iphone-ultra-iphone-18-pro-max-watch-12-and-more">"Surprise and shine"</a> event officially set for Wednesday, September 9</p></div></div><p>Apple's annual iPhone event has been <a href="https://www.tomsguide.com/phones/iphones/apple-event-is-official-for-september-9-iphone-ultra-iphone-18-pro-max-watch-12-and-more">officially confirmed to take place on September 9</a>, and it could easily be the biggest Apple event in years. We're expecting a slew of new devices to be unveiled as John Ternus takes the reins as Apple's new CEO. </p><p>And all of that is before we discuss the launch of the long-rumored, much-hyped foldable <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-rumors-everything-you-need-to-know-about-the-foldable-iphone">iPhone Ultra</a> — Apple's first attempt at a <a href="https://www.tomsguide.com/best-picks/best-foldable-phones">foldable phone</a>. It's not clear when the device will actually launch, but at the very least, its reveal is almost certainly right around the corner. </p><p>In all, Apple is expected to launch up to eight different devices, from the expected new iPhones and upgraded Apple Watches to several new smart home-focused products. </p><p>Here's what we expect to see at <a href="https://www.tomsguide.com/phones/iphones/apple-event-is-official-for-september-9-iphone-ultra-iphone-18-pro-max-watch-12-and-more?hasComeFromProof=true">Apple's 'Surprise and shine' September event</a>, and when it's kicking off</p><h2 id="when-is-the-iphone-18-event">When is the iPhone 18 event?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2066px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="fnjPCS2zVb2TZ4GCnuqwK5" name="Apple suprise and shine" alt="Apple fall event 2026." src="https://cdn.mos.cms.futurecdn.net/fnjPCS2zVb2TZ4GCnuqwK5.jpg" mos="" align="middle" fullscreen="" width="2066" height="1162" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>Apple has <strong>officially confirmed its "Surprise and shine" event will be held on Wednesday, September 9 at Apple Park</strong>. Although Apple normally holds its events on a Tuesday, the Labor Day weekend means it's being moved forward to Wednesday.</p><p>This has happened in the past, and the reasoning is that Apple doesn't want to ask attendees to travel on a holiday weekend. For example, the <a href="https://www.tomsguide.com/reviews/iphone-14">iPhone 14</a> launched on Wednesday, September 7, following Labor Day on Monday 5, and that'll likely be the case this year too.</p><p>The <strong>event is set to take place at 10am PT / 1pm ET / 6pm BST</strong> and will livestreamed on various social media platforms as well as Apple's own site.</p><p>iPhones usually go up for pre-order the Friday following the Apple event, but this year that's September 11. Bloomberg's Mark Gurman has reported that pre-orders will be <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-and-iphone-18-pro-pre-order-date-just-tipped-heres-when-you-can-get-yours">pushed to Saturday, September 12</a>.</p><p>This year's edition will be hosted by incoming CEO John Ternus, and it looks like his tenure will start with a bang.</p><h2 class="article-body__section" id="section-new-iphones"><span>New iPhones</span></h2><h3 id="iphone-18-pro-and-pro-max">iPhone 18 Pro and Pro Max</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UHNuNim9UYH2mmGNTn3S4V" name="Apple-iPhone 18 Pro-A20-Pro_LIST" alt="Apple iPhone 18 Pro and the A20 Pro chip" src="https://cdn.mos.cms.futurecdn.net/UHNuNim9UYH2mmGNTn3S4V.jpg" mos="" align="middle" fullscreen="" width="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/Apple)</span></figcaption></figure><p>This year, Apple is expected to launch only its <a href="https://www.tomsguide.com/phones/iphones/iphone-18-rumors-everything-we-know-so-far">iPhone 18 Pro</a> and <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-max-rumors-and-leaks-everything-we-know-so-far">iPhone 18 Pro Max</a> models. The entry-level iPhone 18 and iPhone 18 Plus reportedly <a href="https://www.tomsguide.com/phones/iphones/iphone-18-release-schedule-could-be-changing-but-this-rumor-makes-no-sense">won't debut until next spring</a> alongside the iPhone 18e model.</p><p>The iPhone 18 Pro and iPhone 18 Pro Max are both tipped to feature Apple's new 2nm A20 Pro chip, a smaller Dynamic Island and Apple's new C2 modem. And both phones should get larger batteries.</p><p>However, the iPhone 18 Pro Max might have some exclusive upgrades, including a variable aperture camera and longer zoom. For more see our <a href="https://www.tomsguide.com/phones/iphones/3-big-reasons-to-buy-iphone-18-pro-max-over-the-iphone-18-pro-this-year">iPhone 18 Pro vs Pro Max</a> comparison.</p><p>Unforutnately, <a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-could-see-absolutely-massive-price-hike-according-to-researchers">analysts have predicted</a> that the new iPhone 18 Pros could cost between $200 and $350 higher than the iPhone 17 Pro family. </p><h3 id="iphone-ultra">iPhone Ultra</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aUJZxCfMMXC3m2nKLVB2dV" name="iPhone Fold final design 1" alt="iPhone Fold design" src="https://cdn.mos.cms.futurecdn.net/aUJZxCfMMXC3m2nKLVB2dV.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Majin Bu on X)</span></figcaption></figure><p>The biggest announcement of the Apple event is sure to be the debut of the foldable iPhone Ultra.</p><p>Leaks have suggested that it will have more of a wider shape, similar to the <a href="https://www.tomsguide.com/phones/samsung-phones/samsung-galaxy-z-fold-8-review-wide-for-the-win">Galaxy Z Fold 8</a>, with a <a href="https://www.tomsguide.com/phones/iphones/iphone-fold-specs-potentially-revealed-heres-how-it-would-stack-up-against-samsung-galaxy-z-fold-8">7.8-inch foldable display and a 5.5-inch cover screen</a>. It could measure between 4.5 and 4.8mm when unfolded, making it one of the thinnest iPhones ever.</p><p>It's rumored to sport a 4,883 mAh battery, the A20 Pro chipset, Apple's in-house C2 modem, and a Touch ID fingerprint scanner in the power button. The rear is expected to come with a pair of 48MP cameras, horizontally positioned like the back of the iPhone Air, alongside two separate 24MP selfie cameras — one for each screen.</p><p>Apple reportedly delayed releasing a folding iPhone until it could eliminate the crease in the display, but whether it's accomplished this remains to be seen. Even Samsung and Oppo, despite their long lead time, have only <a href="https://www.tomsguide.com/phones/samsung-phones/comparing-galaxy-z-fold-8-vs-oppo-find-n6-crease">had moderate success</a>.</p><p>One downside is that the iPhone Ultra could be very expensive, costing anywhere from <a href="https://www.tomsguide.com/phones/iphones/usd1-299-iphone-18-pro-max-analyst-now-predicts-usd200-price-hike-for-apples-next-flagship-phone">$2,000 to 2,500</a>. And a recent leak suggests that<a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-leakers-reveal-new-details-around-release-and-extremely-limited-availability"> iPhone Ultra may not be immediately available, </a>with only "extremely limited" availability.</p><p>Just keep in mind that the <a href="https://www.tomsguide.com/phones/iphones/iphone-ultra-trade-offs-the-biggest-things-youre-not-getting-versus-iphone-18-pro">iPhone Ultra could have a number of trade-offs</a> versus the iPhone 18 Pro, including the lack of a telephoto zoom.</p><h2 class="article-body__section" id="section-airpods-ultra"><span>AirPods Ultra</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:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kXLZ6v6RAcuyoeV3PftvHP" name="AirPods Pro" alt="AirPods Pro" src="https://cdn.mos.cms.futurecdn.net/kXLZ6v6RAcuyoeV3PftvHP.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Regan Coule/Tom's Guide)</span></figcaption></figure><p>These ones are a little surprising as based on a <a href="https://www.tomsguide.com/audio/airpods/gurman-ai-airpods-with-cameras-just-tipped-for-2027-launch-in-flurry-of-new-releases">previous Gurman report</a>, we weren't expecting the AirPods Ultra until 2027 at the earliest. However,<a href="https://www.youtube.com/watch?v=wbBy1KXj5aM" target="_blank"> rumor aggregator AppleTrack</a> just reported this week that the Ultra version of AirPods will launch at the iPhone event. </p><div class="see-more see-more--clipped"><figure><blockquote class="twitter-tweet hawk-ignore" data-lang="en" cite="https://twitter.com/cantworkitout/status/2089522745184760112"><p lang="en" dir="ltr">Here is your first look at AirPods with Cameras in action using Visual Intelligence.This video file came from macOS 26.7 RC pic.twitter.com/yo7RI4MCeu<a href="https://twitter.com/cantworkitout/status/2089522745184760112">August 18, 2026</a></p></blockquote></figure><div class="see-more__filter"></div></div><p>The big change with these headphones is they will allegedly<a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-with-cameras-are-nearly-ready-heres-4-features-you-need-to-know"> feature a camera</a> that will feed into Siri to allow it to "see" the world. </p><p>It would utilize a version of Visual Intelligence to let you ask Siri questions about your surroundings or get turn-by-turn directions from the more <a href="https://www.tomsguide.com/phones/iphones/i-spent-a-week-with-siri-ai-here-are-my-honest-pros-and-cons">powerful AI Siri </a>releasing this fall.</p><p>The <a href="https://www.tomsguide.com/audio/airpods/airpods-ultra-with-cameras-just-appeared-in-leaked-video-and-the-privacy-debate-has-already-started?hasComeFromProof=true">AirPods Ultra has also just appeared in a leaked video</a> buried within the macOS Tahoe 26.7 release candidate, and it shows how they'll help you identify objects in the real world. </p><h2 class="article-body__section" id="section-apple-watch-12-and-apple-watch-ultra-4"><span>Apple Watch 12 and Apple Watch Ultra 4</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LPjRVZraPvxRyivUNsMQaG" name="Apple Watch Series 11" alt="Apple Watch Series 11" src="https://cdn.mos.cms.futurecdn.net/LPjRVZraPvxRyivUNsMQaG.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>Unlike the iPhones, we know a bit less about the next Apple Watches. We are expecting two new models in the <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-series-12-everything-we-know-so-far">Apple Watch Series 12</a> and the <a href="https://www.tomsguide.com/wellness/smartwatches/apple-watch-ultra-4-these-are-the-upgrades-apple-needs-to-make-to-remain-the-smartwatch-king">Apple Watch Ultra 4</a>.</p><p>The leaks have been sparse, but Apple is reportedly not bringing many upgrades this year. The biggest change might be a <a href="https://www.tomsguide.com/wellness/smartwatches/leaked-apple-watch-12-design-change-is-the-opposite-of-an-upgrade-and-that-has-me-worried">new kind of band attachment</a>. This does mean that your old bands might not be compatible with the new watches.</p><p>Reportedly, Apple could <a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">offer a ceramic case option</a> for the first time in years. It should also sport new health and fitness-related upgrades. We already know that <a href="https://www.tomsguide.com/wellness/smartwatches/watchos-27-all-the-new-features-coming-to-apple-watch-later-this-year#section-watchos-27-new-health-and-fitness-features">watchOS 27</a> will bring new AI features, an updated Workout Buddy and more useful data tracking. Plus, it should feature better integration with the upgraded Siri AI which we've already seen in the <a href="https://www.tomsguide.com/phones/iphones/ios-27-public-beta-is-now-live-9-features-you-need-to-try-first">iOS 27 beta</a>.</p><h2 class="article-body__section" id="section-smart-home"><span>Smart Home</span></h2><p>Apple's smart home ambitions and the rumors surrounding them need to be taken with a massive grain of salt. Leaks have suggested Apple is planning on introducing a suite of smart home devices starting with a revamped HomePod and an AI-powered Apple TV 4K streaming device. </p><p>Those rumors have existed for more than two years and largely feed into the company's promises around the AI-powered Siri.</p><p>However, this year with iOS 27, Apple may finally have the chatbot that was promised. If it actually releases and works as pitched, this year could actually be the start of Apple Home. </p><h3 id="homepod-home-hub">HomePod Home hub</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Maj5caC4eG4rJYyquwrf3L" name="HomePod with screen edit" alt="HomePod with display concept render" src="https://cdn.mos.cms.futurecdn.net/Maj5caC4eG4rJYyquwrf3L.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Yanko Design)</span></figcaption></figure><p>Bloomberg's Mark Gurman, long a reporter of the rumored HomePod updates, <a href="https://www.bloomberg.com/news/newsletters/2026-08-09/apple-explores-apple-watch-shake-up-company-prepares-for-september-iphone-event" target="_blank">believes </a>that a new <a href="https://www.tomsguide.com/reviews/apple-homepod-mini">HomePod mini</a> could launch this September alongside the rumored HomePod Home hub.</p><p>Several different iterations of this device have been rumored, including one with a <a href="https://www.tomsguide.com/home/smart-home/apples-next-big-thing-could-be-a-dollar1000-home-robot-ipad-combo">$1,000 robot arm</a>. The most common version involves a <a href="https://www.tomsguide.com/home/smart-home/apple-tipped-to-launch-ai-wall-tablet-to-control-your-home-in-march-2025-everything-we-know-so-far">wall-mounted touchpad</a> or a HomePod speaker with a touch display about the size of an iPad mini. </p><p>This device would have a FaceTime camera for video calls and presence sensors to initiate smart home routines. </p><h3 id="homepod-mini-2-2">HomePod mini 2</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qzn6VBsNWsuQVgzKVVPvhA" name="Apple HomePod mini midnight.jpg" alt="Apple HomePod mini in midnight color" src="https://cdn.mos.cms.futurecdn.net/qzn6VBsNWsuQVgzKVVPvhA.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Apple)</span></figcaption></figure><p>A  <a href="https://www.tomsguide.com/home/smart-home/new-apple-tv-and-homepod-mini-reportedly-launching-in-2025-with-apple-networking-chip">new HomePod mini</a> might also debut, but it might not be as big of an upgrade as the Hub. Most rumors suggest that it will feature a new chip and audio improvements.</p><p>To be clear, this one feels even more far-fetched than the Hub; the HomePod mini 2 has been on our <a href="https://www.tomsguide.com/news/hoping-for-a-homepod-mini-2-to-go-with-the-new-homepod-dont-hold-your-breath">rumor radar since at least 2023</a>. The current version debuted in 2020, and like the next device, is long overdue for an update.</p><h3 id="apple-tv-4k-2026">Apple TV 4K 2026</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3840px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="ueDcVd52GdTw3wgnvCuAkU" name="Google TV Streamer vs Apple TV 4K-3" alt="Google TV Streamer vs Apple TV 4K" src="https://cdn.mos.cms.futurecdn.net/ueDcVd52GdTw3wgnvCuAkU.jpg" mos="" align="middle" fullscreen="" width="3840" height="2156" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Like the HomePod series, Apple hasn't released a new version of the <a href="https://www.tomsguide.com/reviews/apple-tv-4k-2022">Apple TV 4K</a> since 2022. We're long overdue for an upgrade — even if it's only a more powerful chip for stronger performance. </p><p>Previously, it was suggested that a new Apple TV could feature <a href="https://www.tomsguide.com/entertainment/streaming-devices/new-apple-tv-4k-biggest-upgrades-just-leaked-ahead-of-launch">the A17 Pro chip</a>, but it's more likely it'll get the new A20 or, to save some money, last year's A19. The device should also get the <a href="https://www.tomsguide.com/phones/iphones/new-iphone-18-pro-chips-camera-upgrades-and-more-leaked-by-analyst-heres-what-we-know">rumored N2 chip </a>for better Wi-Fi and Bluetooth connectivity. </p><p>Outside of chips, the big upgrade would be Siri AI, which could let you connect the Apple TV as a hub for smart devices. One rumor suggested that Apple might release a <a href="https://www.bloomberg.com/news/newsletters/2024-04-07/apple-aapl-future-products-could-include-robots-and-smart-home-push-lupinmnahttps:/www.bloomberg.com/news/newsletters/2024-04-07/apple-aapl-future-products-could-include-robots-and-smart-home-push-lupinmna" target="_blank">set-top box with a camera,</a> which would allow for FaceTime calls and gesture-based controls.</p><p>Will you be tuning in to Apple's event next month? Let me know which device you're most hyped for in the comments.</p><h2 class="article-body__section" id="section-what-we-don-t-expect-to-see"><span>What we don't expect to see</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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aRTWJedXhSdn4zKRv2Rn9e" name="Apple iPhone 17 review" alt="Apple iPhone 17 review." src="https://cdn.mos.cms.futurecdn.net/aRTWJedXhSdn4zKRv2Rn9e.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: Tom's Guide / John Velasco)</span></figcaption></figure><p>Even with a huge event like this, there are still going to be devices that <a href="https://www.tomsguide.com/phones/iphones/apples-iphone-18-pro-launch-event-is-coming-but-dont-expect-these-5-products-to-appear">won't make an appearance</a>. Due to staggered release schedules and supply-chain timing, Apple is spreading its hardware roadmap across multiple release windows over the next year. As a result, the following devices are unlikely to make any sort of appearance at the event.</p><ul><li><strong>iPhone 18:</strong> Apple is reportedly holding back the base model until early 2027 to drive early demand toward the Pro models.</li><li><strong>AirPods Ultra:</strong> Rumored AirPods with cameras for Siri AI context are reportedly delayed until 2027 due to software and supply-chain bottlenecks.</li><li><strong>New Macs:</strong> Upgraded M6 MacBooks and touchscreen models are typically saved for dedicated computing announcements in October or later.</li><li><strong>OLED iPad mini:</strong> The next refresh of the compact tablet is slated for an October arrival rather than competing for stage time with the new iPhones.</li><li><strong>Apple Watch SE (4th gen):</strong> The entry-level wearable update will sit out this event, with Apple likely to focus on the Series 12 and Ultra 4 instead.</li></ul><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/phones/iphones/iphone-ultra-leakers-reveal-new-details-around-release-and-extremely-limited-availability">iPhone Ultra leakers reveal new details around release and 'extremely limited' availability</a></li><li><a href="https://www.tomsguide.com/phones/the-5-accessories-that-transformed-my-iphone-17-pro-max-for-under-usd50">The 5 accessories that transformed my iPhone 17 Pro Max for under $50</a></li><li><a href="https://www.tomsguide.com/news/ultimate-back-to-school-guide">Ultimate back to school guide 2026: Laptops, Chromebooks, headphones and more</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ With its suave looks, class-leading battery life and phenomenal sound, the Marshall Stockwell III has become my go-to Bluetooth speaker ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Marshall has been on a roll in 2026, adding to its lineups of headphones, home speakers, and now, portable Bluetooth speakers. For the past couple of weeks, I’ve been testing the Marshall Stockwell III which succeeds the seven-year-old Stockwell II — and it has quickly become my go-to speaker for almost everything.</p><p>The Stockwell III improves on its predecessor by doubling the battery life (over 40 hours now), being more waterproof thanks to its IP55 rating, and delivering punchier bass, brighter treble, and incredible 360° sound. Though a little expensive and slightly heavy for a portable speaker, those feel like minor issues in an otherwise compelling (and beautiful) package.</p><p>So, is this the <a href="https://www.tomsguide.com/us/best-bluetooth-speakers,review-1982.html">best portable Bluetooth speaker</a> for you? Read my full Marshall Stockwell III review to find out.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-specs"><span>Marshall Stockwell III review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.marshall.com/gb/en/product/stockwell-iii" target="_blank" rel="nofollow">$249 / £199</a></p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP55</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3 with SBC, AAC and LC3, 3.5mm and USB-C wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multi-speaker connectivity</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Output power</strong></p></td><td  ><p>1x 65W Class D amplifier (3-inch woofer), 2x 31W Class D amplifiers (two 1.75-inch wide-band drivers)</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>54Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Range</strong></p></td><td  ><p>10m / 32.8ft</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>40 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>7.1 x 6.4 x 2.9 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.9lbs</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black and Brass, Cream</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-price-availability"><span>Marshall Stockwell III 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="aEjTrwiHyg39dANWmMDziJ" name="Marshall_Stockwell_III_Speaker_010.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/aEjTrwiHyg39dANWmMDziJ.jpg" 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 Marshall Stockwell III was first announced on 9 June, 2026, over seven years after its predecessor, the Marshall Stockwell II. Available to buy from 18 August, the Stockwell III has a retail price of <a href="https://www.marshall.com/gb/en/product/stockwell-iii" target="_blank" rel="nofollow">$249 / £199</a> — same as the second-gen model at launch in 2019. The Stockwell III comes in Black or Cream, and both have brass accents.</p><p>This is a premium portable Bluetooth speaker, and it finds itself competing with the likes of the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/ultimate-ears-everboom-review">Ultimate Ears Everboom</a> ($249) and the <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> ($299). Though a little expensive, it’s still cheaper than the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/i-just-tested-this-portable-bluetooth-speaker-and-it-could-last-you-forever">Bang & Olufsen Beosound A1 3rd Gen</a> which costs a whopping $399. The Stockwell III is worth the investment if you want 40+ hours of battery and beautiful sound stuffed into a compact (and stunning) package.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-design"><span>Marshall Stockwell III 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="PRJYvqsoqRKGpUWrmCbzaK" name="Marshall_Stockwell_III_Speaker_005.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/PRJYvqsoqRKGpUWrmCbzaK.jpg" 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 Marshall Stockwell III is, in a nutshell, a ridiculously sexy speaker, and <em>the </em>portable speaker to get if you care about aesthetics above all else. Not much has changed between the Stockwell III and its predecessor when it comes to portability. The third-gen model measures 7.1 x 6.4 x 2.9 inches and weighs 2.9lbs. To make it easier to carry around, Marshall has included a guitar-esque, velvet carry strap in the box, which can be attached to the Stockwell III’s sides.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XNVzTW39bgryrU2HX2EoZK" name="Marshall_Stockwell_III_Speaker_012.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/XNVzTW39bgryrU2HX2EoZK.jpg" 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>Given its hefty weight, the Stockwell III isn’t the most portable speaker around. The Ultimate Ears Everboom is lighter at 2.1lbs, as is the Bang & Olufsen Beosound A1 3rd Gen which weighs 1.27lbs. The Stockwell III’s strap makes it slightly easier to carry around, but if you want an even lighter speaker but with Marshall’s looks, the <a href="https://www.tomsguide.com/audio/marshal-emberton-iii-review">Emberton III</a> ($149, 1.5lbs) is a standout choice.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3009px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="99LhosA5GaBQhamtjfA9oC" name="Marshall_Stockwell_III-2" alt="Marshall Stockwell III Bluetooth portable speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/99LhosA5GaBQhamtjfA9oC.jpg" mos="" align="middle" fullscreen="" width="3009" height="1693" 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 Stockwell III looks more premium compared to its predecessor, too. While the Stockwell II featured white accents, the Stockwell III now features brass branding (on either black or cream), keeping in line with the brand’s other current-gen speakers — and headphones, too, like the <a href="https://www.tomsguide.com/audio/headphones/marshall-milton-anc-review">Milton ANC</a> ($229) and the <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review">Monitor III </a>($349). I much prefer this look as the black/cream and gold combo makes the speaker stand out in a crowded market.</p><p>Build quality is solid, thanks to metal mesh grilles and the textured silicone outer sleeve. You’re getting bang for your buck with the Stockwell III.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LfwoDyV3HgiQB9RGxD3vYK" name="Marshall_Stockwell_III_Speaker_016.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/LfwoDyV3HgiQB9RGxD3vYK.jpg" 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>But why should you buy the Stockwell III instead of Marshall’s other more portable (ergo, lightweight) speakers? The Stockwell III is a mid-sized, premium Bluetooth speaker that’s clearly designed for both home (given the Placement Compensation feature within the app) and light outdoor use (given its IP55 rating).</p><p>It’s got a more luxurious feel to it than the ultra-portable Emberton III, for instance, but it’s smaller than a massive party speaker, like the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-bromley-450-review">Bromley 450</a> ($799). If you want a small speaker to travel with, and one that can withstand the elements, get the IP67-rated Emberton III. But if you want a mid-sized speaker that isn’t as expensive or bulky as the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-kilburn-iii-review">Kilburn III </a>($379), get the Stockwell III.</p><h2 id="weatherproofing-5">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kdmxA8EAKBuqKA2pnxB6nC" name="Marshall_Stockwell_III-3" alt="Marshall Stockwell III Bluetooth portable speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/kdmxA8EAKBuqKA2pnxB6nC.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" 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>Marshall has made significant improvements to the Stockwell III’s durability as the third-gen model is now IP55-rated — the Stockwell II was only IPX4-rated. IP55 means the Stockwell III is protected from dust and is fairly water resistant, though you can’t submerge it underwater like you can the IP67-rated Ultimate Ears Everboom.</p><p>IP55 should suffice for most scenarios, but be mindful of using it in wet conditions repeatedly as any gadget’s waterproofing wears down over time.</p><h2 id="controls-10">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="yYPayj7Q9yfiEWByt7xCaK" name="Marshall_Stockwell_III_Speaker_014.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/yYPayj7Q9yfiEWByt7xCaK.jpg" 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 buttons and dials are located on the Stockwell III’s top plate, and you’ll be familiar with them if you’ve used any of Marshall’s other speakers, like the <a href="https://www.tomsguide.com/audio/smart-speakers/marshall-acton-iv-review">Acton IV</a> ($299). Even if you aren’t, getting to grips with the controls is quick and easy. All the buttons and dials give great tactile feedback when pressed or turned. </p><p>There’s a Source button for swapping between Bluetooth and AUX, and a customizable ‘M’ button that, by default, activates Spotify Tap so you can start playing music without opening the app on your phone; playback joystick; and three amp-like knobs for adjusting volume, treble and bass. Alongside these, Marshall has also added a handy battery indicator.</p><h2 id="connectivity-8">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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JmLp57pYSrVCXwTN2mbKaK" name="Marshall_Stockwell_III_Speaker_008.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/JmLp57pYSrVCXwTN2mbKaK.jpg" 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 Stockwell III features a 3.5mm jack and a USB-C port for wired playback, as well as Bluetooth 5.3 for wireless connectivity. Over Bluetooth, you get support for SBC, AAC and LC3 codecs — the latter delivers high efficiency and better sound quality at low bitrates. This also gives it a leg-up over the Ultimate Ears Everboom which supports SBC and AAC only.</p><p>While the Stockwell II featured traditional multi-speaker pairing with other Stockwell IIs, the Stockwell III is Auracast-enabled — and that’s better as it enables multiple devices to connect to a single audio source without pairing. The third-gen model utilizes Bluetooth LE to essentially connect to an unlimited number of other Auracast-enabled Marshall speakers, such as the Emberton III or other Stockwell IIIs.</p><p>You also get multipoint connectivity, which means that you can connect two different Bluetooth devices to the Stockwell III simultaneously. I did this with my <a href="https://www.tomsguide.com/reviews/macbook-air-2022-m2">MacBook Air M2</a> and <a href="https://www.tomsguide.com/phones/google-pixel-phones/google-pixel-10-pro-xl-review">Google Pixel 10 Pro XL</a> and it worked seamlessly.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-sound-quality"><span>Marshall Stockwell III 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="cJuZ93UVeGvdiFfv48yRVJ" name="Marshall_Stockwell_III_Speaker_011.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/cJuZ93UVeGvdiFfv48yRVJ.jpg" 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 Marshall Stockwell III is very loud for its size — it’s one of the best-sounding portable Bluetooth speakers around. Under the hood, the Stockwell III is fitted with a 65W Class D amplifier to power its 3-inch woofer, and two 31W Class D amplifiers for its dual 1.75-inch wide-band drivers (versus two 5W tweeters and a single 10W woofer on the Stockwell II). The speaker covers a frequency response range of 54Hz to 20KHz.</p><p>Marshall has clearly spared no expense when it comes to stuffing the Stockwell III to the brim — and the result is simply outstanding sound. To test the speaker, I listened to a range of songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles. ‘<a href="https://open.qobuz.com/track/398890244" target="_blank" rel="nofollow">2515</a>’ by Wasia Project, which has a strong, pulsating bassline acting as the track’s backbone throughout, sounded sublime. The song’s dark electronic pulse came through with real weight, while the bass remained tight and controlled, and never became muddy — even at high volumes.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4ygwMmfKZdmg5ZcjJJTdZK" name="Marshall_Stockwell_III_Speaker_006.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/4ygwMmfKZdmg5ZcjJJTdZK.jpg" 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 where Marshall’s ‘Dynamic Loudness’ comes into play. The Stockwell III borrows this from the <a href="https://www.tomsguide.com/audio/smart-speakers/marshall-stanmore-iv-review">Stanmore IV</a> ($399) and the Acton IV, and it basically ensures that sound remains balanced and distortion-free regardless of the volume. Playing ‘2515’ at 80% volume didn’t overwhelm the speaker, as the lows maintained their warmth and richness.</p><p>I then listened to ‘<a href="https://open.qobuz.com/track/64405603" target="_blank" rel="nofollow">Mariners Apartment Complex</a>’ by Lana Del Rey to test the soundstage — and sure enough, the Stockwell III didn’t falter. Lana’s vocals sat front and center, with her words remaining clear and never bleeding into one another. There was a great balance between her remarkable vocals and the acoustic guitar and piano, and the strings sounded textured and defined. Also, the True Stereophonic 360° — found on the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-bromley-750-review">Bromley 750</a> ($1,299) and Bromley 450 party speakers — filled the room and the song sounded consistent when I moved around, giving the track an effortless sense of space and depth.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="eBSeskbUXDHVeK2sMS28XK" name="Marshall_Stockwell_III_Speaker_007.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/eBSeskbUXDHVeK2sMS28XK.jpg" 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>Lastly, I played ‘<a href="https://open.qobuz.com/track/76767860" target="_blank" rel="nofollow">Papua New Guinea</a>’ by The Future Sound of London, an electronic, ambient techno track. The driving basslines, swirling synths, breakbeat-style percussion and oscillating keyboard loops all came together to create a layered production, and the Stockwell III reproduced the dreamy nature extremely well. The treble felt vibrant and nuanced without becoming fatiguing. My partner and I couldn’t help but move along to the song, like we were at a rave.</p><p><em>Listen along to my review with the Marshall Stockwell III testing playlist:</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/68631732?zone=GB-en"></iframe><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-app"><span>Marshall Stockwell III review: App</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="e2sBa5XjBvQ7LiP4hrBGMK" name="Marshall_Stockwell_III_Speaker_013.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/e2sBa5XjBvQ7LiP4hrBGMK.jpg" 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 Marshall Stockwell III is accompanied by the <a href="https://www.marshall.com/gb/en/support/speakers/learn/app-marshall-bluetooth" target="_blank" rel="nofollow">Marshall Bluetooth app</a>, available for free on iOS and Android. It’s the same app used by the British brand’s headphones and earbuds, and it’s your one-stop shop for adjusting the EQ or choosing a preset, updating the firmware, enabling Auracast, and customizing the ‘M’ button. The app sports a clean interface and is extremely easy to use.</p><p>You also get Placement Compensation, where you can select whether the speaker is close to an edge or corner, or flush against a wall. Once you’ve chosen your desired settings, the Stockwell III then corrects itself for any nearby reflective surfaces that may affect the sound quality. This feature has been borrowed from the Stanmore IV and Acton IV, which are plug-in home speakers. It’s handy to have if you’re keeping the Stanmore III in one place but given that it’s a portable Bluetooth speaker, I highly doubt you’ll be keeping it stationary.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/3FaDrwhoRqRafhHtBRDbtD.jpg" alt="Marshall Bluetooth app screenshots" /><figcaption><small role="credit">Marshall / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MTA84CGuSfcTvezwcHYxxD.jpg" alt="Marshall Bluetooth app screenshots" /><figcaption><small role="credit">Marshall / Tom's Guide</small></figcaption></figure></figure><p>There’s a Source Mix option, too, which is new. It basically enables you to play music over Bluetooth <em>and </em>via USB-C at the same time. I can’t really think of a use case for this, but I suppose if you want to feel like a DJ, you can.</p><p>One thing I find surprising, though, is that the Stockwell III uses the Marshall Bluetooth app and not the new <a href="https://play.google.com/store/apps/details?id=com.marshallgroup.marshall&hl=en_GB" target="_blank" rel="nofollow">Marshall</a> app (came out in 2026 before the Stockwell III) which its fourth-gen home speakers utilize. Wouldn’t it make sense to have all of its speakers in one app? I’m personally not a fan of having two separate apps for different devices. Maybe it’s because the Stockwell III was actually created before the Stanmore IV and Acton IV were. Perhaps this is something Marshall will fix later. This isn’t a complaint, anyway, just an observation.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-battery-life"><span>Marshall Stockwell III 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:960px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aEjTrwiHyg39dANWmMDziJ" name="Marshall_Stockwell_III_Speaker_010.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/v2/t:108,l:439,cw:960,ch:540,q:80/aEjTrwiHyg39dANWmMDziJ.jpg" 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>Marshall has fitted a bigger battery into the Stockwell III, and the speaker can now run for over 40 hours on a single charge. That’s double the Stockwell II’s battery life. The Stockwell III trumps its competition too, as the Sonos Play is rated for 24 hours, and the Ultimate Ears Everboom is rated for 20. Even the pricier Bang & Olufsen Beosound A1 3rd Gen is rated for just 24 hours.</p><p>In my testing, I played at 50% volume over multiple listening sessions. The total time it took for the Stockwell III to run out of battery was just over 40 hours, keeping in line with Marshall’s claims.</p><p>Similar to the Sonos Play, the Stockwell III doubles as a power bank and you can use a USB-C cable to charge your smartphone, laptop and other devices. This is a great feature to have in a portable speaker as you don’t need to worry about finding a socket to charge your phone if you’re, say, out on a hike.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-verdict"><span>Marshall Stockwell III 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="XCL39H5yGe7zmJRd3sMxXK" name="Marshall_Stockwell_III_Speaker_002.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/XCL39H5yGe7zmJRd3sMxXK.jpg" 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 when I thought Marshall speakers couldn’t get any better, the iconic amp manufacturer comes out with something like the Stockwell III. This is an excellent portable speaker that delivers far more sound than its compact design suggests. Though it isn’t cheap — just shy of $250 — its combination of rich bass, vibrant treble and clear mids make a strong case for the price.</p><p>The Stockwell III also packs 40 hours of battery life, IP55 durability, and immersive 360° sound into a slim profile. While it’s still not the lightest portable speaker around, the included velvet strap makes it easier to carry around.</p><p>I can’t help but describe the speaker’s sound as genuinely astounding. If I had to take one speaker with me on my travels or to picnics outdoors, it would be the Stockwell III. At the time of writing, it’s Marshall’s best portable Bluetooth speaker right now.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/bluetooth-speakers/marshall-stockwell-iii-review</link>
                                                                            <description>
                            <![CDATA[ The Marshall Stockwell III is a beautiful portable speaker that can get very loud, and it boasts meaty bass and bright treble. It also packs a 40-hour battery. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">xwoMoRxtdVoyRuYBP9aMQM</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/9bzmRUT3eRzPgjBmwSYNgC-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 18 Aug 2026 06: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.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/9bzmRUT3eRzPgjBmwSYNgC-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Marshall Stockwell III Bluetooth portable speaker in black and brass]]></media:description>                                                            <media:text><![CDATA[Marshall Stockwell III Bluetooth portable speaker in black and brass]]></media:text>
                                <media:title type="plain"><![CDATA[Marshall Stockwell III Bluetooth portable speaker in black and brass]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/9bzmRUT3eRzPgjBmwSYNgC-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Marshall has been on a roll in 2026, adding to its lineups of headphones, home speakers, and now, portable Bluetooth speakers. For the past couple of weeks, I’ve been testing the Marshall Stockwell III which succeeds the seven-year-old Stockwell II — and it has quickly become my go-to speaker for almost everything.</p><p>The Stockwell III improves on its predecessor by doubling the battery life (over 40 hours now), being more waterproof thanks to its IP55 rating, and delivering punchier bass, brighter treble, and incredible 360° sound. Though a little expensive and slightly heavy for a portable speaker, those feel like minor issues in an otherwise compelling (and beautiful) package.</p><p>So, is this the <a href="https://www.tomsguide.com/us/best-bluetooth-speakers,review-1982.html">best portable Bluetooth speaker</a> for you? Read my full Marshall Stockwell III review to find out.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-specs"><span>Marshall Stockwell III review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.marshall.com/gb/en/product/stockwell-iii" target="_blank" rel="nofollow">$249 / £199</a></p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP55</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3 with SBC, AAC and LC3, 3.5mm and USB-C wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multi-speaker connectivity</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Output power</strong></p></td><td  ><p>1x 65W Class D amplifier (3-inch woofer), 2x 31W Class D amplifiers (two 1.75-inch wide-band drivers)</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>54Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Range</strong></p></td><td  ><p>10m / 32.8ft</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>40 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>7.1 x 6.4 x 2.9 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.9lbs</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black and Brass, Cream</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-price-availability"><span>Marshall Stockwell III 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="aEjTrwiHyg39dANWmMDziJ" name="Marshall_Stockwell_III_Speaker_010.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/aEjTrwiHyg39dANWmMDziJ.jpg" 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 Marshall Stockwell III was first announced on 9 June, 2026, over seven years after its predecessor, the Marshall Stockwell II. Available to buy from 18 August, the Stockwell III has a retail price of <a href="https://www.marshall.com/gb/en/product/stockwell-iii" target="_blank" rel="nofollow">$249 / £199</a> — same as the second-gen model at launch in 2019. The Stockwell III comes in Black or Cream, and both have brass accents.</p><p>This is a premium portable Bluetooth speaker, and it finds itself competing with the likes of the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/ultimate-ears-everboom-review">Ultimate Ears Everboom</a> ($249) and the <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> ($299). Though a little expensive, it’s still cheaper than the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/i-just-tested-this-portable-bluetooth-speaker-and-it-could-last-you-forever">Bang & Olufsen Beosound A1 3rd Gen</a> which costs a whopping $399. The Stockwell III is worth the investment if you want 40+ hours of battery and beautiful sound stuffed into a compact (and stunning) package.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-design"><span>Marshall Stockwell III 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="PRJYvqsoqRKGpUWrmCbzaK" name="Marshall_Stockwell_III_Speaker_005.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/PRJYvqsoqRKGpUWrmCbzaK.jpg" 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 Marshall Stockwell III is, in a nutshell, a ridiculously sexy speaker, and <em>the </em>portable speaker to get if you care about aesthetics above all else. Not much has changed between the Stockwell III and its predecessor when it comes to portability. The third-gen model measures 7.1 x 6.4 x 2.9 inches and weighs 2.9lbs. To make it easier to carry around, Marshall has included a guitar-esque, velvet carry strap in the box, which can be attached to the Stockwell III’s sides.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XNVzTW39bgryrU2HX2EoZK" name="Marshall_Stockwell_III_Speaker_012.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/XNVzTW39bgryrU2HX2EoZK.jpg" 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>Given its hefty weight, the Stockwell III isn’t the most portable speaker around. The Ultimate Ears Everboom is lighter at 2.1lbs, as is the Bang & Olufsen Beosound A1 3rd Gen which weighs 1.27lbs. The Stockwell III’s strap makes it slightly easier to carry around, but if you want an even lighter speaker but with Marshall’s looks, the <a href="https://www.tomsguide.com/audio/marshal-emberton-iii-review">Emberton III</a> ($149, 1.5lbs) is a standout choice.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3009px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="99LhosA5GaBQhamtjfA9oC" name="Marshall_Stockwell_III-2" alt="Marshall Stockwell III Bluetooth portable speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/99LhosA5GaBQhamtjfA9oC.jpg" mos="" align="middle" fullscreen="" width="3009" height="1693" 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 Stockwell III looks more premium compared to its predecessor, too. While the Stockwell II featured white accents, the Stockwell III now features brass branding (on either black or cream), keeping in line with the brand’s other current-gen speakers — and headphones, too, like the <a href="https://www.tomsguide.com/audio/headphones/marshall-milton-anc-review">Milton ANC</a> ($229) and the <a href="https://www.tomsguide.com/audio/headphones/marshall-monitor-iii-review">Monitor III </a>($349). I much prefer this look as the black/cream and gold combo makes the speaker stand out in a crowded market.</p><p>Build quality is solid, thanks to metal mesh grilles and the textured silicone outer sleeve. You’re getting bang for your buck with the Stockwell III.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LfwoDyV3HgiQB9RGxD3vYK" name="Marshall_Stockwell_III_Speaker_016.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/LfwoDyV3HgiQB9RGxD3vYK.jpg" 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>But why should you buy the Stockwell III instead of Marshall’s other more portable (ergo, lightweight) speakers? The Stockwell III is a mid-sized, premium Bluetooth speaker that’s clearly designed for both home (given the Placement Compensation feature within the app) and light outdoor use (given its IP55 rating).</p><p>It’s got a more luxurious feel to it than the ultra-portable Emberton III, for instance, but it’s smaller than a massive party speaker, like the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-bromley-450-review">Bromley 450</a> ($799). If you want a small speaker to travel with, and one that can withstand the elements, get the IP67-rated Emberton III. But if you want a mid-sized speaker that isn’t as expensive or bulky as the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-kilburn-iii-review">Kilburn III </a>($379), get the Stockwell III.</p><h2 id="weatherproofing-5">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kdmxA8EAKBuqKA2pnxB6nC" name="Marshall_Stockwell_III-3" alt="Marshall Stockwell III Bluetooth portable speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/kdmxA8EAKBuqKA2pnxB6nC.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" 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>Marshall has made significant improvements to the Stockwell III’s durability as the third-gen model is now IP55-rated — the Stockwell II was only IPX4-rated. IP55 means the Stockwell III is protected from dust and is fairly water resistant, though you can’t submerge it underwater like you can the IP67-rated Ultimate Ears Everboom.</p><p>IP55 should suffice for most scenarios, but be mindful of using it in wet conditions repeatedly as any gadget’s waterproofing wears down over time.</p><h2 id="controls-10">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="yYPayj7Q9yfiEWByt7xCaK" name="Marshall_Stockwell_III_Speaker_014.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/yYPayj7Q9yfiEWByt7xCaK.jpg" 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 buttons and dials are located on the Stockwell III’s top plate, and you’ll be familiar with them if you’ve used any of Marshall’s other speakers, like the <a href="https://www.tomsguide.com/audio/smart-speakers/marshall-acton-iv-review">Acton IV</a> ($299). Even if you aren’t, getting to grips with the controls is quick and easy. All the buttons and dials give great tactile feedback when pressed or turned. </p><p>There’s a Source button for swapping between Bluetooth and AUX, and a customizable ‘M’ button that, by default, activates Spotify Tap so you can start playing music without opening the app on your phone; playback joystick; and three amp-like knobs for adjusting volume, treble and bass. Alongside these, Marshall has also added a handy battery indicator.</p><h2 id="connectivity-8">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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JmLp57pYSrVCXwTN2mbKaK" name="Marshall_Stockwell_III_Speaker_008.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/JmLp57pYSrVCXwTN2mbKaK.jpg" 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 Stockwell III features a 3.5mm jack and a USB-C port for wired playback, as well as Bluetooth 5.3 for wireless connectivity. Over Bluetooth, you get support for SBC, AAC and LC3 codecs — the latter delivers high efficiency and better sound quality at low bitrates. This also gives it a leg-up over the Ultimate Ears Everboom which supports SBC and AAC only.</p><p>While the Stockwell II featured traditional multi-speaker pairing with other Stockwell IIs, the Stockwell III is Auracast-enabled — and that’s better as it enables multiple devices to connect to a single audio source without pairing. The third-gen model utilizes Bluetooth LE to essentially connect to an unlimited number of other Auracast-enabled Marshall speakers, such as the Emberton III or other Stockwell IIIs.</p><p>You also get multipoint connectivity, which means that you can connect two different Bluetooth devices to the Stockwell III simultaneously. I did this with my <a href="https://www.tomsguide.com/reviews/macbook-air-2022-m2">MacBook Air M2</a> and <a href="https://www.tomsguide.com/phones/google-pixel-phones/google-pixel-10-pro-xl-review">Google Pixel 10 Pro XL</a> and it worked seamlessly.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-sound-quality"><span>Marshall Stockwell III 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="cJuZ93UVeGvdiFfv48yRVJ" name="Marshall_Stockwell_III_Speaker_011.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/cJuZ93UVeGvdiFfv48yRVJ.jpg" 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 Marshall Stockwell III is very loud for its size — it’s one of the best-sounding portable Bluetooth speakers around. Under the hood, the Stockwell III is fitted with a 65W Class D amplifier to power its 3-inch woofer, and two 31W Class D amplifiers for its dual 1.75-inch wide-band drivers (versus two 5W tweeters and a single 10W woofer on the Stockwell II). The speaker covers a frequency response range of 54Hz to 20KHz.</p><p>Marshall has clearly spared no expense when it comes to stuffing the Stockwell III to the brim — and the result is simply outstanding sound. To test the speaker, I listened to a range of songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for audiophiles. ‘<a href="https://open.qobuz.com/track/398890244" target="_blank" rel="nofollow">2515</a>’ by Wasia Project, which has a strong, pulsating bassline acting as the track’s backbone throughout, sounded sublime. The song’s dark electronic pulse came through with real weight, while the bass remained tight and controlled, and never became muddy — even at high volumes.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4ygwMmfKZdmg5ZcjJJTdZK" name="Marshall_Stockwell_III_Speaker_006.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/4ygwMmfKZdmg5ZcjJJTdZK.jpg" 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 where Marshall’s ‘Dynamic Loudness’ comes into play. The Stockwell III borrows this from the <a href="https://www.tomsguide.com/audio/smart-speakers/marshall-stanmore-iv-review">Stanmore IV</a> ($399) and the Acton IV, and it basically ensures that sound remains balanced and distortion-free regardless of the volume. Playing ‘2515’ at 80% volume didn’t overwhelm the speaker, as the lows maintained their warmth and richness.</p><p>I then listened to ‘<a href="https://open.qobuz.com/track/64405603" target="_blank" rel="nofollow">Mariners Apartment Complex</a>’ by Lana Del Rey to test the soundstage — and sure enough, the Stockwell III didn’t falter. Lana’s vocals sat front and center, with her words remaining clear and never bleeding into one another. There was a great balance between her remarkable vocals and the acoustic guitar and piano, and the strings sounded textured and defined. Also, the True Stereophonic 360° — found on the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/marshall-bromley-750-review">Bromley 750</a> ($1,299) and Bromley 450 party speakers — filled the room and the song sounded consistent when I moved around, giving the track an effortless sense of space and depth.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="eBSeskbUXDHVeK2sMS28XK" name="Marshall_Stockwell_III_Speaker_007.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/eBSeskbUXDHVeK2sMS28XK.jpg" 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>Lastly, I played ‘<a href="https://open.qobuz.com/track/76767860" target="_blank" rel="nofollow">Papua New Guinea</a>’ by The Future Sound of London, an electronic, ambient techno track. The driving basslines, swirling synths, breakbeat-style percussion and oscillating keyboard loops all came together to create a layered production, and the Stockwell III reproduced the dreamy nature extremely well. The treble felt vibrant and nuanced without becoming fatiguing. My partner and I couldn’t help but move along to the song, like we were at a rave.</p><p><em>Listen along to my review with the Marshall Stockwell III testing playlist:</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/68631732?zone=GB-en"></iframe><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-app"><span>Marshall Stockwell III review: App</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="e2sBa5XjBvQ7LiP4hrBGMK" name="Marshall_Stockwell_III_Speaker_013.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/e2sBa5XjBvQ7LiP4hrBGMK.jpg" 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 Marshall Stockwell III is accompanied by the <a href="https://www.marshall.com/gb/en/support/speakers/learn/app-marshall-bluetooth" target="_blank" rel="nofollow">Marshall Bluetooth app</a>, available for free on iOS and Android. It’s the same app used by the British brand’s headphones and earbuds, and it’s your one-stop shop for adjusting the EQ or choosing a preset, updating the firmware, enabling Auracast, and customizing the ‘M’ button. The app sports a clean interface and is extremely easy to use.</p><p>You also get Placement Compensation, where you can select whether the speaker is close to an edge or corner, or flush against a wall. Once you’ve chosen your desired settings, the Stockwell III then corrects itself for any nearby reflective surfaces that may affect the sound quality. This feature has been borrowed from the Stanmore IV and Acton IV, which are plug-in home speakers. It’s handy to have if you’re keeping the Stanmore III in one place but given that it’s a portable Bluetooth speaker, I highly doubt you’ll be keeping it stationary.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/3FaDrwhoRqRafhHtBRDbtD.jpg" alt="Marshall Bluetooth app screenshots" /><figcaption><small role="credit">Marshall / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MTA84CGuSfcTvezwcHYxxD.jpg" alt="Marshall Bluetooth app screenshots" /><figcaption><small role="credit">Marshall / Tom's Guide</small></figcaption></figure></figure><p>There’s a Source Mix option, too, which is new. It basically enables you to play music over Bluetooth <em>and </em>via USB-C at the same time. I can’t really think of a use case for this, but I suppose if you want to feel like a DJ, you can.</p><p>One thing I find surprising, though, is that the Stockwell III uses the Marshall Bluetooth app and not the new <a href="https://play.google.com/store/apps/details?id=com.marshallgroup.marshall&hl=en_GB" target="_blank" rel="nofollow">Marshall</a> app (came out in 2026 before the Stockwell III) which its fourth-gen home speakers utilize. Wouldn’t it make sense to have all of its speakers in one app? I’m personally not a fan of having two separate apps for different devices. Maybe it’s because the Stockwell III was actually created before the Stanmore IV and Acton IV were. Perhaps this is something Marshall will fix later. This isn’t a complaint, anyway, just an observation.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-battery-life"><span>Marshall Stockwell III 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:960px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aEjTrwiHyg39dANWmMDziJ" name="Marshall_Stockwell_III_Speaker_010.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/v2/t:108,l:439,cw:960,ch:540,q:80/aEjTrwiHyg39dANWmMDziJ.jpg" 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>Marshall has fitted a bigger battery into the Stockwell III, and the speaker can now run for over 40 hours on a single charge. That’s double the Stockwell II’s battery life. The Stockwell III trumps its competition too, as the Sonos Play is rated for 24 hours, and the Ultimate Ears Everboom is rated for 20. Even the pricier Bang & Olufsen Beosound A1 3rd Gen is rated for just 24 hours.</p><p>In my testing, I played at 50% volume over multiple listening sessions. The total time it took for the Stockwell III to run out of battery was just over 40 hours, keeping in line with Marshall’s claims.</p><p>Similar to the Sonos Play, the Stockwell III doubles as a power bank and you can use a USB-C cable to charge your smartphone, laptop and other devices. This is a great feature to have in a portable speaker as you don’t need to worry about finding a socket to charge your phone if you’re, say, out on a hike.</p><h2 class="article-body__section" id="section-marshall-stockwell-iii-review-verdict"><span>Marshall Stockwell III 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="XCL39H5yGe7zmJRd3sMxXK" name="Marshall_Stockwell_III_Speaker_002.JPG" alt="Marshall Stockwell III portable Bluetooth speaker in black and brass" src="https://cdn.mos.cms.futurecdn.net/XCL39H5yGe7zmJRd3sMxXK.jpg" 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 when I thought Marshall speakers couldn’t get any better, the iconic amp manufacturer comes out with something like the Stockwell III. This is an excellent portable speaker that delivers far more sound than its compact design suggests. Though it isn’t cheap — just shy of $250 — its combination of rich bass, vibrant treble and clear mids make a strong case for the price.</p><p>The Stockwell III also packs 40 hours of battery life, IP55 durability, and immersive 360° sound into a slim profile. While it’s still not the lightest portable speaker around, the included velvet strap makes it easier to carry around.</p><p>I can’t help but describe the speaker’s sound as genuinely astounding. If I had to take one speaker with me on my travels or to picnics outdoors, it would be the Stockwell III. At the time of writing, it’s Marshall’s best portable Bluetooth speaker right now.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I ditched my $300 Bose earbuds this much cheaper open pair — and it’s the best choice I could have made ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I’ve made it my personal mission to find the best pair of open-earbuds on the market. I find myself reaching for my open-earbuds almost daily, and I think everybody should have a pair in their audio gear rotation. But how do you know what to look for or how much to spend?</p><p>First, let’s address the elephant in the room — open-earbuds won’t be for everyone. They’re for a very specific person, such as office workers, runners and gym-goers who need to hear the ambient sound around them at all times.</p><p>When I first started testing open buds, I was skeptical. I was so used to in-ear buds that I wasn’t sure why anyone would want to sacrifice ANC (and indeed audio quality) in their daily listening. However, once you realize that having situational awareness and superior comfort in the office and at the gym is a game changer, I’m telling you, there’s no going back.</p><p>This style of earbud is still in its infancy, with the first pair of truly open earbuds being released by Bose in 2021. So this means that the space is still constantly improving, with brands innovating their open-ear styles and finding ways to replicate the sound quality of in-ear buds. My personal favorite style is the clip, but you can also buy hook-style earbuds that are better suited for high-intensity activities like running.</p><h2 id="open-earbud-sound">Open-earbud sound</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jv9GhcC8k4Rncv8j58ayTo" name="IMG_8448.jpg" alt="Bose Ultra Open Earbuds" src="https://cdn.mos.cms.futurecdn.net/jv9GhcC8k4Rncv8j58ayTo.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>In short, since you don’t get a seal around the ear canal, which you get with regular earbuds, sound waves escape. Open-earbuds rely on directing sound towards your eardrum, but there’s a lot of sound leakage. This means you can lose some frequencies, and with the addition of ambient sound, it can be tricky to listen to your music in detail. </p><p>Low frequencies that contribute to bass performance are often lost as they need trapped air pressure to effectively drive the sound. This means you’ll lose the low-end impact, according to <a href="https://shokz.com/blogs/news/sacrifice-sound-quality-for-open-ear-headphones"><u>Shokz</u></a>.</p><p>Recently, I’ve been using the <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds">Bose Ultra Open Earbuds</a>. Bose is known for awesome bass performance in its products from speakers to headphones — but what about open-earbuds? Well, I can tell you they do a great job. Despite the obvious hurdles, these buds deliver a bass performance I’d be pleased with from regular earbuds. </p><p>However, for their $299 price, I was disappointed by the lack of detail I got from the Bose buds. I had high expectations because of the premium price tag and the sleek look, but ultimately, I felt let down. The sound was almost unnatural, and the bass priority was making my music sound muffled in the mid and high frequencies. </p><p>So while I enjoy listening with the Bose Ultra Open Earbuds, they’re far from perfect. And if I’m paying $300, I need them to be perfect.</p><div class="product"><a data-dimension112="ab59d28c-97fc-11f1-af34-83884271eba2" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216" href="https://www.amazon.com/Bose-Immersive-Wireless-OpenAudio-Awareness/dp/B0CPFV77W4" 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="5FmYCaXFJd8WbRzhikbuoU" name="bose-ultra-open-earbuds--bluetooth-open--44681bf1-d130-44b1-abb9-c13c8f4fc094.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/5FmYCaXFJd8WbRzhikbuoU.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose Ultra Open Earbuds feature an open ear design that lets you hear the world around you while still enjoying great sound quality, complete with spatial audio support. The design is flexible and comfortable and you get up to 27 hours of battery life with the included charging case. <a class="view-deal button" href="https://www.amazon.com/Bose-Immersive-Wireless-OpenAudio-Awareness/dp/B0CPFV77W4" target="_blank" rel="nofollow" data-dimension112="ab59d28c-97fc-11f1-af34-83884271eba2" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216">View Deal</a></p></div><h2 id="my-perfect-open-earbuds">My perfect open earbuds</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2hCruKDDjJsXwLkVqHAmr4" name="Bose & Beyerdynamic" alt="Beyerdynamic Amiron ZERO and Bose Ultra Open Earbuds" src="https://cdn.mos.cms.futurecdn.net/2hCruKDDjJsXwLkVqHAmr4.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>But fear not. My quest to find the best open-earbuds continued. And after months of testing, I think I found them. The buds to beat all other buds. The Beyerdynamic Amiron ZERO. </p><p>Reaching for a lower-priced competitor after being let down by a premium flagship product felt like a bit of a Hail Mary. But after spending months wishing I could get some real balanced detail from a pair of open-earbuds, the Beyerdynamics have delivered. </p><p>They are everything I’ve been asking for from open-earbuds. </p><p>Firstly, these buds have the same clip design as the Bose, but are far more comfortable as they’re highly flexible and don’t move much during workouts. The Beyerdynamics also have a higher durability rating, with dust and splash protection, whereas the Bose are only protected from water splashes. The studio tuning of the Beyerdynamics lends itself well to crisp highs and a natural sound, whereas the Bose buds sound almost artificial and manufactured. </p><p>The Bose buds are tuned to please a large consumer base, but the detailed studio tuning of the Beyerdynamics allows you to immerse yourself in the music wherever you are. </p><div class="product"><a data-dimension112="ab59d43a-97fc-11f1-8d57-e9171a857c3d" data-action="Deal Block" data-label="Amiron ZERO" data-dimension48="Amiron ZERO" data-dimension25="$159" href="https://www.amazon.com/beyerdynamic-Headphones-Microphone-Lightweight-Comfortable/dp/B0FLYB1L38" 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="ec96Jvhaa9Eem9zoDArZ3L" name="beyerdynamic-amiron-zero-ultra-open-ear--7a361042-4078-4485-942d-1d272b8bc2de.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ec96Jvhaa9Eem9zoDArZ3L.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beyerdynamic Amiron ZERO open-earbuds are the ones to beat. They have excellent sound quality with impressive detail, and the simple app with customizable EQ settings gave me complete control. They’re comfortable to wear for long periods, and have an IP54 durability rating. The battery life is 20 hours (less than many other models on the market), but this isn’t a deal breaker.<a class="view-deal button" href="https://www.amazon.com/beyerdynamic-Headphones-Microphone-Lightweight-Comfortable/dp/B0FLYB1L38" target="_blank" rel="nofollow" data-dimension112="ab59d43a-97fc-11f1-8d57-e9171a857c3d" data-action="Deal Block" data-label="Amiron ZERO" data-dimension48="Amiron ZERO" data-dimension25="$159">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XbD9yW"></div>                            </div>                            <script src="https://kwizly.com/embed/XbD9yW.js" async></script><h2 id="keeping-it-real">Keeping it real</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5164px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UKJET84TmeGrTGpaGk9Xk4" name="Bose & Beyerdynamic" alt="Beyerdynamic Amiron ZERO and Bose Ultra Open Earbuds" src="https://cdn.mos.cms.futurecdn.net/UKJET84TmeGrTGpaGk9Xk4.jpg" mos="" align="middle" fullscreen="" width="5164" height="2905" attribution="" endorsement="" class="inline"></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 the sound tuning of the Beyerdynamics makes them my winner, there are some trade-offs. The Bose buds have a longer total battery life of 27 hours in comparison to the 20 hours of the Beyerdynamics, and if you care about special features like head-tracking spatial audio, the Bose will be your bag. </p><p>Also, when you look at the cases side by side, there’s not much difference, so either pair will do the job well for portability.</p><p>The takeaway here is that spending more doesn’t always get you the best sound. Sure, the Bose buds <em>are</em> some of the best you can buy, but for a natural, balanced sound that translates well across a wider variety of genres, I can’t recommend the Beyerdynamic Amiron ZEROs enough. </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/entertainment/music-streaming/heres-how-to-get-the-most-out-of-your-spotify-premium-subscription-everything-included-for-usd12-a-month-that-you-should-be-taking-advantage-of"><strong>How to get the most out of Spotify Premium — everything included for $13 a month you should be using</strong></a></li><li><a href="I’ve been living with the Galaxy Z Fold 8 for 3 weeks — and I was wrong"><strong>I’ve been living with the Galaxy Z Fold 8 for 3 weeks — and I was wrong</strong></a></li><li><a href="https://www.tomsguide.com/wellness/fitness-trackers/i-walked-5-000-steps-with-the-fitbit-air-vs-garmin-cirqa-round-2-we-have-a-new-champ"><strong>I walked 5,000 steps with the Fitbit Air vs Garmin Cirqa — Round 2: We have a new champ</strong></a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/i-ditched-my-usd300-bose-earbuds-for-a-usd180-pair-and-its-the-best-choice-i-could-have-made</link>
                                                                            <description>
                            <![CDATA[ After testing the £300 Bose Ultra Open Earbuds, I was left disappointed, but I switched to a cheaper pair by Beyerdynamic, and everything changed ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">J792EWbZP9q3LfqHgd7yVY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/HedS42fRmsmCGB8DQY2JK4-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 15 Aug 2026 08:45:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Earbuds]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/HedS42fRmsmCGB8DQY2JK4-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Beyerdynamic Amiron ZERO and Bose Ultra Open Earbuds]]></media:description>                                                            <media:text><![CDATA[Beyerdynamic Amiron ZERO and Bose Ultra Open Earbuds]]></media:text>
                                <media:title type="plain"><![CDATA[Beyerdynamic Amiron ZERO and Bose Ultra Open Earbuds]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/HedS42fRmsmCGB8DQY2JK4-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I’ve made it my personal mission to find the best pair of open-earbuds on the market. I find myself reaching for my open-earbuds almost daily, and I think everybody should have a pair in their audio gear rotation. But how do you know what to look for or how much to spend?</p><p>First, let’s address the elephant in the room — open-earbuds won’t be for everyone. They’re for a very specific person, such as office workers, runners and gym-goers who need to hear the ambient sound around them at all times.</p><p>When I first started testing open buds, I was skeptical. I was so used to in-ear buds that I wasn’t sure why anyone would want to sacrifice ANC (and indeed audio quality) in their daily listening. However, once you realize that having situational awareness and superior comfort in the office and at the gym is a game changer, I’m telling you, there’s no going back.</p><p>This style of earbud is still in its infancy, with the first pair of truly open earbuds being released by Bose in 2021. So this means that the space is still constantly improving, with brands innovating their open-ear styles and finding ways to replicate the sound quality of in-ear buds. My personal favorite style is the clip, but you can also buy hook-style earbuds that are better suited for high-intensity activities like running.</p><h2 id="open-earbud-sound">Open-earbud sound</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jv9GhcC8k4Rncv8j58ayTo" name="IMG_8448.jpg" alt="Bose Ultra Open Earbuds" src="https://cdn.mos.cms.futurecdn.net/jv9GhcC8k4Rncv8j58ayTo.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>In short, since you don’t get a seal around the ear canal, which you get with regular earbuds, sound waves escape. Open-earbuds rely on directing sound towards your eardrum, but there’s a lot of sound leakage. This means you can lose some frequencies, and with the addition of ambient sound, it can be tricky to listen to your music in detail. </p><p>Low frequencies that contribute to bass performance are often lost as they need trapped air pressure to effectively drive the sound. This means you’ll lose the low-end impact, according to <a href="https://shokz.com/blogs/news/sacrifice-sound-quality-for-open-ear-headphones"><u>Shokz</u></a>.</p><p>Recently, I’ve been using the <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds">Bose Ultra Open Earbuds</a>. Bose is known for awesome bass performance in its products from speakers to headphones — but what about open-earbuds? Well, I can tell you they do a great job. Despite the obvious hurdles, these buds deliver a bass performance I’d be pleased with from regular earbuds. </p><p>However, for their $299 price, I was disappointed by the lack of detail I got from the Bose buds. I had high expectations because of the premium price tag and the sleek look, but ultimately, I felt let down. The sound was almost unnatural, and the bass priority was making my music sound muffled in the mid and high frequencies. </p><p>So while I enjoy listening with the Bose Ultra Open Earbuds, they’re far from perfect. And if I’m paying $300, I need them to be perfect.</p><div class="product"><a data-dimension112="ab59d28c-97fc-11f1-af34-83884271eba2" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216" href="https://www.amazon.com/Bose-Immersive-Wireless-OpenAudio-Awareness/dp/B0CPFV77W4" 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="5FmYCaXFJd8WbRzhikbuoU" name="bose-ultra-open-earbuds--bluetooth-open--44681bf1-d130-44b1-abb9-c13c8f4fc094.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/5FmYCaXFJd8WbRzhikbuoU.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose Ultra Open Earbuds feature an open ear design that lets you hear the world around you while still enjoying great sound quality, complete with spatial audio support. The design is flexible and comfortable and you get up to 27 hours of battery life with the included charging case. <a class="view-deal button" href="https://www.amazon.com/Bose-Immersive-Wireless-OpenAudio-Awareness/dp/B0CPFV77W4" target="_blank" rel="nofollow" data-dimension112="ab59d28c-97fc-11f1-af34-83884271eba2" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$216">View Deal</a></p></div><h2 id="my-perfect-open-earbuds">My perfect open earbuds</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2hCruKDDjJsXwLkVqHAmr4" name="Bose & Beyerdynamic" alt="Beyerdynamic Amiron ZERO and Bose Ultra Open Earbuds" src="https://cdn.mos.cms.futurecdn.net/2hCruKDDjJsXwLkVqHAmr4.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>But fear not. My quest to find the best open-earbuds continued. And after months of testing, I think I found them. The buds to beat all other buds. The Beyerdynamic Amiron ZERO. </p><p>Reaching for a lower-priced competitor after being let down by a premium flagship product felt like a bit of a Hail Mary. But after spending months wishing I could get some real balanced detail from a pair of open-earbuds, the Beyerdynamics have delivered. </p><p>They are everything I’ve been asking for from open-earbuds. </p><p>Firstly, these buds have the same clip design as the Bose, but are far more comfortable as they’re highly flexible and don’t move much during workouts. The Beyerdynamics also have a higher durability rating, with dust and splash protection, whereas the Bose are only protected from water splashes. The studio tuning of the Beyerdynamics lends itself well to crisp highs and a natural sound, whereas the Bose buds sound almost artificial and manufactured. </p><p>The Bose buds are tuned to please a large consumer base, but the detailed studio tuning of the Beyerdynamics allows you to immerse yourself in the music wherever you are. </p><div class="product"><a data-dimension112="ab59d43a-97fc-11f1-8d57-e9171a857c3d" data-action="Deal Block" data-label="Amiron ZERO" data-dimension48="Amiron ZERO" data-dimension25="$159" href="https://www.amazon.com/beyerdynamic-Headphones-Microphone-Lightweight-Comfortable/dp/B0FLYB1L38" 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="ec96Jvhaa9Eem9zoDArZ3L" name="beyerdynamic-amiron-zero-ultra-open-ear--7a361042-4078-4485-942d-1d272b8bc2de.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ec96Jvhaa9Eem9zoDArZ3L.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beyerdynamic Amiron ZERO open-earbuds are the ones to beat. They have excellent sound quality with impressive detail, and the simple app with customizable EQ settings gave me complete control. They’re comfortable to wear for long periods, and have an IP54 durability rating. The battery life is 20 hours (less than many other models on the market), but this isn’t a deal breaker.<a class="view-deal button" href="https://www.amazon.com/beyerdynamic-Headphones-Microphone-Lightweight-Comfortable/dp/B0FLYB1L38" target="_blank" rel="nofollow" data-dimension112="ab59d43a-97fc-11f1-8d57-e9171a857c3d" data-action="Deal Block" data-label="Amiron ZERO" data-dimension48="Amiron ZERO" data-dimension25="$159">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XbD9yW"></div>                            </div>                            <script src="https://kwizly.com/embed/XbD9yW.js" async></script><h2 id="keeping-it-real">Keeping it real</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5164px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="UKJET84TmeGrTGpaGk9Xk4" name="Bose & Beyerdynamic" alt="Beyerdynamic Amiron ZERO and Bose Ultra Open Earbuds" src="https://cdn.mos.cms.futurecdn.net/UKJET84TmeGrTGpaGk9Xk4.jpg" mos="" align="middle" fullscreen="" width="5164" height="2905" attribution="" endorsement="" class="inline"></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 the sound tuning of the Beyerdynamics makes them my winner, there are some trade-offs. The Bose buds have a longer total battery life of 27 hours in comparison to the 20 hours of the Beyerdynamics, and if you care about special features like head-tracking spatial audio, the Bose will be your bag. </p><p>Also, when you look at the cases side by side, there’s not much difference, so either pair will do the job well for portability.</p><p>The takeaway here is that spending more doesn’t always get you the best sound. Sure, the Bose buds <em>are</em> some of the best you can buy, but for a natural, balanced sound that translates well across a wider variety of genres, I can’t recommend the Beyerdynamic Amiron ZEROs enough. </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/entertainment/music-streaming/heres-how-to-get-the-most-out-of-your-spotify-premium-subscription-everything-included-for-usd12-a-month-that-you-should-be-taking-advantage-of"><strong>How to get the most out of Spotify Premium — everything included for $13 a month you should be using</strong></a></li><li><a href="I’ve been living with the Galaxy Z Fold 8 for 3 weeks — and I was wrong"><strong>I’ve been living with the Galaxy Z Fold 8 for 3 weeks — and I was wrong</strong></a></li><li><a href="https://www.tomsguide.com/wellness/fitness-trackers/i-walked-5-000-steps-with-the-fitbit-air-vs-garmin-cirqa-round-2-we-have-a-new-champ"><strong>I walked 5,000 steps with the Fitbit Air vs Garmin Cirqa — Round 2: We have a new champ</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Never lose a podcast highlight again thanks to this Spotify feature ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I love podcasts and listen to them whenever I get the opportunity. There’s so much good stuff out there from amazing <a href="https://www.tomsguide.com/entertainment/im-obsessed-with-these-5-pop-culture-podcasts-and-one-just-won-a-golden-globe">pop culture </a>podcasts to those for a <a href="https://www.tomsguide.com/entertainment/streaming/im-a-true-crime-junkie-and-these-5-podcasts-are-the-only-ones-that-actually-keep-me-up-at-night">true crime junkie</a>. But often I come across snippets of great content within a podcast that I want to save and listen to again later. </p><p>Instead of re-listening to an entire episode or scrubbing around hoping to find the right spot, I now use Spotify’s clip feature to save key moments directly to my account or share them with others.</p><p>It saves me a whole ton of time. All I have to do is head into the 'Your Library' section and my saved clips are ready to play. It’s super easy to do and works with both Free and Premium accounts, making it available for everyone.</p><section class="howto-block">                    <h2>1. Go to the Now Playing view</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/qw6shW8yoKRSwLhtLXhC6W.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/qw6shW8yoKRSwLhtLXhC6W.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>First of all, <strong>open the Spotify app</strong> and <strong>find a podcast </strong>that you want to listen to. Then, when you <strong>play the podcast</strong>, <strong>go to the Now Playing view</strong>. Just <strong>tap the mini-player</strong> bar at the bottom of the screen.</p></p>                </section><section class="howto-block">                    <h2>2. Tap the scissors</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/456VuRpToKviKwFAocGHyV.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/456VuRpToKviKwFAocGHyV.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>Look beneath the podcast controls and <strong>tap on the scissors icon</strong>. Doing so will display Spotify’s editing section</p></p>                </section><section class="howto-block">                    <h2>3. Use the share option (alternative method)</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/XJ8ckZpBgcJrWhpLNsQysV.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/XJ8ckZpBgcJrWhpLNsQysV.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>You can also find the editing function if you <strong>tap the Share button</strong>. Just <strong>select Edit start and end point</strong> when the option appears.</p></p>                </section><section class="howto-block">                    <h2> 4. Drag the sliders</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/8DG4UanZ3hcZWczunaftmV.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/8DG4UanZ3hcZWczunaftmV.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>The current podcast section will be highlighted using a green box. <strong>Drag the sliders to the left and right</strong> to select the part of the podcast you want to capture.</p></p>                </section><section class="howto-block">                    <h2>5. Save the clip</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/SkJbhqVKEvSYdMmNz8VhfV.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/SkJbhqVKEvSYdMmNz8VhfV.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>When you are satisfied you’ve got the bit you need,<strong> tap Save</strong>. The clip will end up in your Library within the Your Clips folder.</p><p>If you used the alternative share option in step three above,<strong> tap Continue</strong> and <strong>select your preferred app</strong> to share the clip with others. The share option also lets you share a full episode, chapter or timestamp.</p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Wwnb9e"></div>                            </div>                            <script src="https://kwizly.com/embed/Wwnb9e.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/entertainment/music-streaming/spotify-has-3-hidden-features-most-people-dont-know-about-heres-how-to-use-them">Spotify has features most people don't know about — here's how to use them</a></li><li><a href="https://www.tomsguide.com/entertainment/music-streaming/i-found-3-hidden-spotify-features-that-completely-changed-how-i-listen-to-music">I found 3 Spotify features that completely changed how I listen to music</a></li><li><a href="https://www.tomsguide.com/entertainment/music-streaming/spotify-just-changed-how-concert-tickets-work-and-you-might-already-be-on-the-reserved-list">Spotify just eliminated the worst part of buying concert tickets — and it’s an absolute game-changer</a><strong></strong></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/never-lose-a-podcast-highlight-again-thanks-to-this-spotify-feature</link>
                                                                            <description>
                            <![CDATA[ Quickly save, edit, and share podcast clips in Spotify to build a personalized library of your favorite highlights. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">uXFTsfbduyi67ysMVdmsQc</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/L4JXRRDrGg6AjcFQZcLv3V-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 14 Aug 2026 05:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ David Crookes ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/yQWsHDmJr9eBZrL4xqd3bE.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;David Crookes has been writing professionally for close to 30 years and, as well as a great fondness for gaming, space, film, music, history, health and politics, he&#039;s largely specialized in technology for much of his career. He particularly focuses on Apple devices, having admired the company&#039;s tech since using iMac G3 in 1998. He also developed a fondness for the older Macintosh LC III that he later used in his first job as a newspaper reporter.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Since becoming a freelance writer, he has written for a wide number of publications including the Apple-focussed magazines iCreate and Macworld as well as Micro Mart, Web User, T3, Retro Gamer, MagPi, Computer Shopper, Gadget and 3D Artist. His love of gaming has seen him write for Retro Gamer, GamesTM and Wireframe among others and, despite his love of Apple, he still uses a PC – working for PC Pro and once writing for Total PC Gaming.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;One of his joys is helping others to use technology which has led him to writing many “how to” tutorials explaining many ways of getting the most out of various devices. He&#039;s become an expert user of the iPhone, iPad, Mac, Apple Watch and Apple TV. He also likes to kick back with a gaming session on an PlayStation 5 or Xbox Series X | S while getting his retro fix on an Evercade handheld.&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/L4JXRRDrGg6AjcFQZcLv3V-1280-80.jpg">
                                                            <media:credit><![CDATA[Shutterstock]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Spotify logo on a phone with white headphones resting on the phone]]></media:description>                                                            <media:text><![CDATA[Spotify logo on a phone with white headphones resting on the phone]]></media:text>
                                <media:title type="plain"><![CDATA[Spotify logo on a phone with white headphones resting on the phone]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/L4JXRRDrGg6AjcFQZcLv3V-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I love podcasts and listen to them whenever I get the opportunity. There’s so much good stuff out there from amazing <a href="https://www.tomsguide.com/entertainment/im-obsessed-with-these-5-pop-culture-podcasts-and-one-just-won-a-golden-globe">pop culture </a>podcasts to those for a <a href="https://www.tomsguide.com/entertainment/streaming/im-a-true-crime-junkie-and-these-5-podcasts-are-the-only-ones-that-actually-keep-me-up-at-night">true crime junkie</a>. But often I come across snippets of great content within a podcast that I want to save and listen to again later. </p><p>Instead of re-listening to an entire episode or scrubbing around hoping to find the right spot, I now use Spotify’s clip feature to save key moments directly to my account or share them with others.</p><p>It saves me a whole ton of time. All I have to do is head into the 'Your Library' section and my saved clips are ready to play. It’s super easy to do and works with both Free and Premium accounts, making it available for everyone.</p><section class="howto-block">                    <h2>1. Go to the Now Playing view</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/qw6shW8yoKRSwLhtLXhC6W.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/qw6shW8yoKRSwLhtLXhC6W.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>First of all, <strong>open the Spotify app</strong> and <strong>find a podcast </strong>that you want to listen to. Then, when you <strong>play the podcast</strong>, <strong>go to the Now Playing view</strong>. Just <strong>tap the mini-player</strong> bar at the bottom of the screen.</p></p>                </section><section class="howto-block">                    <h2>2. Tap the scissors</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/456VuRpToKviKwFAocGHyV.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/456VuRpToKviKwFAocGHyV.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>Look beneath the podcast controls and <strong>tap on the scissors icon</strong>. Doing so will display Spotify’s editing section</p></p>                </section><section class="howto-block">                    <h2>3. Use the share option (alternative method)</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/XJ8ckZpBgcJrWhpLNsQysV.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/XJ8ckZpBgcJrWhpLNsQysV.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>You can also find the editing function if you <strong>tap the Share button</strong>. Just <strong>select Edit start and end point</strong> when the option appears.</p></p>                </section><section class="howto-block">                    <h2> 4. Drag the sliders</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/8DG4UanZ3hcZWczunaftmV.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/8DG4UanZ3hcZWczunaftmV.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>The current podcast section will be highlighted using a green box. <strong>Drag the sliders to the left and right</strong> to select the part of the podcast you want to capture.</p></p>                </section><section class="howto-block">                    <h2>5. Save the clip</h2>                    <figure>                            <p class="bordeaux-image-check">                                <img    src="https://cdn.mos.cms.futurecdn.net/SkJbhqVKEvSYdMmNz8VhfV.png"                                        alt="Learn how to edit podcast clips in Spotify"                                        onerror="this.parentNode.replaceChild(window.missingImage(),this)"                                        data-pin-media="https://cdn.mos.cms.futurecdn.net/SkJbhqVKEvSYdMmNz8VhfV.png"                                        class="expandable van-old-layout-image">                            </p><div class="credit">(Image: © Future)</div></figure>                    <p><p>When you are satisfied you’ve got the bit you need,<strong> tap Save</strong>. The clip will end up in your Library within the Your Clips folder.</p><p>If you used the alternative share option in step three above,<strong> tap Continue</strong> and <strong>select your preferred app</strong> to share the clip with others. The share option also lets you share a full episode, chapter or timestamp.</p></p>                </section><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Wwnb9e"></div>                            </div>                            <script src="https://kwizly.com/embed/Wwnb9e.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/entertainment/music-streaming/spotify-has-3-hidden-features-most-people-dont-know-about-heres-how-to-use-them">Spotify has features most people don't know about — here's how to use them</a></li><li><a href="https://www.tomsguide.com/entertainment/music-streaming/i-found-3-hidden-spotify-features-that-completely-changed-how-i-listen-to-music">I found 3 Spotify features that completely changed how I listen to music</a></li><li><a href="https://www.tomsguide.com/entertainment/music-streaming/spotify-just-changed-how-concert-tickets-work-and-you-might-already-be-on-the-reserved-list">Spotify just eliminated the worst part of buying concert tickets — and it’s an absolute game-changer</a><strong></strong></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested a music-lover's $1,499 all-in-one soundbar — and it's not by Samsung, Bose, or Sony ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Bluesound Pulse Cinema is an incredibly interesting soundbar. It’s $1,499, which puts it firmly in the “premium” category, and it’s all-in-one, which means it has two subwoofer channels in the soundbar itself. So, is all of this enough to make it one of the <a href="https://www.tomsguide.com/us/best-soundbar-speakers,review-2004.html">best soundbars</a> available in 2026? </p><p>Unfortunately, not quite. While the overall sound performance is great, a few dialogue issues (and no dialogue mode) prevented it from achieving full marks. Even so, I didn’t have to strain to understand 99% of dialogue — but those rare instances marred my experience. It’s pretty pricey for just a soundbar, and there’s not as much height as I would expect for $1,500. </p><p>It’s still a worthy contender, though, especially if you want a soundbar as part of a wider multi-speaker sound system. Want to find out more? Keep reading this Bluesound Pulse Cinema review. </p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-specs"><span>Bluesound Pulse Cinema 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/Bluesound-Multi-Room-Streaming-Soundbar-Subwoofer/dp/B0FRV83WY2" target="_blank" rel="nofollow">$1,499</a> / <a href="https://www.richersounds.com/bluesound-pulse-cinema/" target="_blank" rel="nofollow">£1,249</a></p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>HDMI, optical, AirPlay, Bluetooth, RCA, USB-A</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>14.8 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>47.1 x 2.9 x 5.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Gray</p></td></tr><tr><td class="firstcol " ><p><strong>Subwoofer</strong></p></td><td  ><p>No, but sub out</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>3.2.2</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-price-availability"><span>Bluesound Pulse Cinema 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="miPq6L2SUXg5PtcXDWo6Yf" name="Bluesound_PulseCinema_soundbar 3.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/miPq6L2SUXg5PtcXDWo6Yf.jpg" 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 Bluesound Pulse Cinema is a pretty pricey soundbar, at <a href="https://www.amazon.com/Bluesound-Multi-Room-Streaming-Soundbar-Subwoofer/dp/B0FRV83WY2" target="_blank" rel="nofollow">$1,499 from Amazon U.S.</a> and <a href="https://www.richersounds.com/bluesound-pulse-cinema/" target="_blank" rel="nofollow">£1,249 from Richer Sounds in the U.K.</a></p><p>For a 3.2.2-channel soundbar, this is quite expensive. I’ve tested the 23-driver, 7.1.4-channel <a href="https://www.tomsguide.com/audio/soundbars/jbl-bar-1300xmk2-review">JBL Bar 1300X MKII</a>, which is only $300 pricier, at $1,699. I’ve also reviewed the <a href="https://www.tomsguide.com/audio/soundbars/samsung-qs90h-review">Samsung QS90H</a> ($899), which is fantastic if you want excellent bass without needing a separate subwoofer. </p><p>If money is no object, I’d recommend checking out the $2,999 <a href="https://www.tomsguide.com/audio/soundbars/yamaha-true-x-surround-90a-review">Yamaha True X Surround 90A</a>, which is the most immersive Dolby Atmos soundbar I’ve ever used. There’s also the fantastic <a href="https://www.tomsguide.com/audio/soundbars/kef-xio-review">KEF XIO</a>, $1,699, which has incredible sound quality and 360° audio. </p><p>At the cheaper end of the spectrum, I’d recommend the $399 <a href="https://www.tomsguide.com/audio/soundbars/ive-officially-found-my-new-favorite-mid-range-soundbar-it-made-me-feel-like-i-was-at-the-movies">Samsung Q600F</a>/H or the $699 <a href="https://www.tomsguide.com/audio/soundbars/denon-home-sound-bar-550-review">Denon Home 550</a> for soundbars with clarity and depth. </p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-design"><span>Bluesound Pulse Cinema 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="36QZoUEqMWhdH2ffbCiAqf" name="Bluesound_PulseCinema_soundbar 5.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/36QZoUEqMWhdH2ffbCiAqf.jpg" 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 Bluesound Pulse Cinema looks like any other soundbar. It’s pretty plain, but in a purposeful way that means it won’t steal attention away from the rest of your living room. Of course, there’s a mesh exterior and an on-bar button control pad. This illuminates red, green, and blue. </p><p>It’s quite long, at 47 inches, so I’d recommend it only for 55-inch TVs and up. Any smaller would look strange. If you’ve a smaller TV, you might be better suited to the 27-inch <a href="https://www.tomsguide.com/audio/soundbars/bose-smart-soundbar-review">Bose Smart Soundbar </a>(2024), or the 26-inch Denon Home 550.</p><p>The soundbar was much lighter than I thought it would be: 14.8 pounds. Compared to the Yamaha True X Surround 90A’s 24-pound heft and the KEF XIO’s 23-pound bulk, this was noticeably easier to move around. You might not even need a friend to help you set it up. </p><h2 id="controls-11">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="JXhBKBCwxd5U2ny6HLXyGf" name="Bluesound_PulseCinema_soundbar 8.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/JXhBKBCwxd5U2ny6HLXyGf.jpg" 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>There’s no remote, so all your controlling is done via the BlueOS app. I found setting this up relatively straightforward. I had to restart it a couple of times, but it worked without any headache from me. I didn’t even have to set up my Wi-Fi network: the app took the information from AirPlay, which was great. No scrabbling around trying to find that pesky piece of paper taped to the back of my router. </p><p>Ideally, there would be a remote, too. What if your phone breaks and then you’re stuck without a soundbar remote? While this likely won’t be an issue 99% of the time, there’s always that 1%. </p><p>As I connected the soundbar via HDMI eARC, I was able to control it via my TV and Blu-ray player remotes, so I didn’t have any issues here. Specific adjustments like treble and bass volume controls were easy to make in the app. </p><h2 id="features-2">Features</h2><p>I was disappointed to see no dialogue mode or dialogue enhancer. I personally wouldn’t buy a soundbar without this, purely because I like the reassurance of being able to enhance my dialogue if required. </p><p>The Pulse Cinema does have “night”, “movie”, and “music” modes. There’s also a “surround upmixer”, “virtualizer”, and “volume leveler”, alongside treble and bass EQ adjustment. The surround upmixer does what it says on the tin — creates a virtual surround sound environment. “Virtualizer” does pretty much the same thing. I didn’t notice “volume leveler” making much of a change, but it’s supposed to make all the soundbar output the same volume. This would be good if you listen to poorly mastered media or if you’re struggling to hear dialogue.</p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-app"><span>Bluesound Pulse Cinema review: App</span></h2><p>The Bluesound Pulse Cinema uses the BlueOS app, which works almost identically to the Denon HEOS app. This is more of a multi-room smart speaker app than the soundbar-specific JBL One app. There’s no room calibration or individual channel adjustment, which is a shame for a $1,499 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:1600px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7WBQvMTcmbVJK3XgpeHoVL" name="bluesound pulse cinema screenshots" alt="three screenshots from the bluesound OS app showing connectivity functions with the pulse cinema soundbar" src="https://cdn.mos.cms.futurecdn.net/7WBQvMTcmbVJK3XgpeHoVL.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Bluesound)</span></figcaption></figure><p>As you can see, the app has TuneIn and integrates music streaming services like TIDAL and Qobuz, making it ideal for building a general multi-speaker home setup. It’s easy to use and easy to set up, so I have no criticisms here. I just wish it had some more soundbar/home cinema-specific modes.</p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-sound-quality"><span>Bluesound Pulse Cinema review: Sound quality</span></h2><p>I test every soundbar in the same room, with “Top Gun: Maverick” on 4K Blu-ray, various other Blu-rays, TV streaming, and music on <a href="https://www.whathifi.com/qobuz/review">Qobuz</a> via AirPlay.</p><h2 id="movies-2">Movies</h2><p>As always, I started with “Top Gun: Maverick”. The opening scene is a goldmine for soundbar testing: high-frequency in-scene sound effects like whirring engines and whooshing ropes, bass-heavy “Danger Zone”, the roaring of jet engines flying overhead. </p><p>I could feel the planes flying around me, but I couldn’t hear them tracking over me. This is a similar issue on Samsung soundbars, namely the QS90H and the Q600F/H. I want height <em>and</em> width! I tested the Yamaha True Sound 90A in the same room, and it offered much more height, which is why it’s the best Dolby Atmos soundbar I’ve ever used — but it <em>is</em> $3,000. I’d also recommend the JBL Bar 1300X MKII as one of the best surround sound experiences — it had plenty of height in the same test, and is only $300 more than the Bluesound. </p><p>Whooshing ropes were clear, and the grinding of Tom Cruise’s wrench was audible in the following scene. The string-heavy soundtrack radiated through the scene like sunlight streaming through cloud cover — but I didn’t get as much width or depth as I wanted. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pGjZSaKJHq3BXkVU2Z7Mjf" name="Bluesound_PulseCinema_soundbar 7.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/pGjZSaKJHq3BXkVU2Z7Mjf.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also noticed some roughness around dialogue, as if the soundbar was struggling to differentiate the bass in Tom Cruise’s voice from the bass in the OST. This was also an issue in “How to Train Your Dragon (2025)”, but I’ll discuss that in detail below. </p><p>However, during action scenes, the bass performance was good. In the first flying scene — Maverick attempting Mach 10 — I could feel the cabin rattling around, the rumbling of the engine right before the explosion. I <em>did</em> want a little more during the explosion, though. </p><p>In the first dogfighting scene, the radioed dialogue was clear, despite the lack of dialogue enhancement mode. The rumbling bass of the jet engines didn’t overpower dialogue. I never struggled to make out speech in this scene. </p><p>I also watched “How to Train Your Dragon (2025)”. Hiccup’s voice-over and the OST were both clear in the opening action scene, but the dragon sound effects were the true standouts here, especially in the grove scene halfway through the movie. When Toothless flew away, the sound swarmed from around me.  </p><p>However, there were unpleasant crackling sounds when the characters were talking and moving. While this might be a “How to Train Your Dragon (2025)” mixing issue, I also experienced some crackling during Tom Cruise’s dialogue in “Top Gun: Maverick”. Amazon reviews mention “bass popping”, but I didn’t experience anything like this.</p><p>I also watched the original Disney “Alice in Wonderland”. This movie is encoded with Dolby Digital rather than true Atmos. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="y7Lp5gU9dysobcDZJsGd4f" name="Bluesound_PulseCinema_soundbar 9.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/y7Lp5gU9dysobcDZJsGd4f.jpg" 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>“Alice in Wonderland” sounded fantastic. The soundbar had no issues switching from music to dialogue to ‘action’ scenes (Alice falling down the rabbit hole). When Alice fell, my stomach actually dropped thanks to the sound effects. The flutes sounded particularly excellent, and I never struggled to hear dialogue — but this isn’t exactly a blockbuster action movie. </p><p>As a final blockbuster test, I watched “The Batman (2022)”. The eerie, tense, string-heavy OST didn’t overpower the dialogue or in-scene TV sounds whilst screeching in the background of the scene. The falling rain was exceptionally wide; I could hear it as if it were actually pouring in the testing room. </p><p>The iconic “Something in the Way” scene was my favorite scene of the movie. I could hear Pattinson’s brooding voiceover clearly, despite the music and motorcycle sound effects. I could also make out the newsreader speech and the rewinding sound effects over the music. This performance was really great — much better than “How to Train Your Dragon (2025)”.</p><h2 id="tv-2">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="WS2qA9Re9XxDXAvVTovBjf" name="Bluesound_PulseCinema_soundbar 6.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/WS2qA9Re9XxDXAvVTovBjf.jpg" 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 playback, I watched “Interview with the Vampire” on basic Netflix. I didn’t notice any crackling or dialogue hissing here. The saloon-inspired soundtrack danced across the soundstage, but never overpowered the dialogue. In-scene sound effects like crickets and crackling fires were clear and pronounced, but layered perfectly beneath dialogue. </p><p>I noticed some compression issues when the scenes switched between voiceover and retrospective scenes. “Interview with the Vampire” is that — an interview, taking place in modern day. The protagonist’s dialogue weaves from “present day” interview answers into the retrospective scenes of which he speaks. During each scene, I didn’t notice any processing issues, but during transitional scenes, some dialogue was compressed. </p><p>Overall, though, I enjoyed this experience. I never struggled to hear dialogue or make out high-frequency sound effects.</p><h2 id="music-2">Music</h2><p>To test music playback, I listened to a range of genres via Qobuz, our <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service for audiophiles</a>. ‘Von dutch (A.G. Cook remix ft. Addison Rae)’ by Charli xcx is an electronic track with processed synths and rolling bass. Right at the end of the track, there’s a spoken section. This section sounded <em>fantastic</em>. I felt like the words were bouncing around the room. </p><p>After, I played ‘I Was Neon’, an indie track with rounded female vocals and slightly crispy guitars. The percussion was a little muted, but not overly so, and the licks of electric guitar after the chorus were particularly melodic. While I wanted a little more control and complexity to the bass performance, at the end of the day, this is a soundbar, not a speaker. This music performance is impressive. </p><p>Lastly, I switched to heavier rock with ‘My God Is The Sun’ by Queens of the Stone Age. As with the Julia Jacklin track, the overall performance was good for a soundbar. Homme’s vocals were clear and propulsive, and the percussion wasn’t tinny. However, I wanted <em>more</em>. More bass, more complexity, more passion. At the end of the day, though, this is a soundbar. And soundbars will never be as impressive as genuine music-focused speakers.</p><p>Overall, for music, this is one of the best soundbars I’ve ever used. It matches the Bose Smart Soundbar (2024) and exceeds the Samsung Q600H and QS90H. </p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-verdict"><span>Bluesound Pulse Cinema 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="G9MedBLyu7tp94V44j3HNf" name="Bluesound_PulseCinema_soundbar 10.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/G9MedBLyu7tp94V44j3HNf.jpg" 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 Bluesound Pulse Cinema is a great soundbar, with impressive width and perhaps the best music on a soundbar I’ve ever experienced. The app is easy to use and straightforward, with no connectivity or setup issues. </p><p>Even so, for it to achieve our coveted 4-star rating, it would need a few changes: no dialogue/bass rumbling issues (as experienced during “Top Gun: Maverick” and “How to Train Your Dragon (2025)”, and more premium soundbar-specific features — or a lower price. When you can spend $300 extra for the near-perfect JBL Bar 1300X MK2, or save $600 and get the all-in-one Samsung QS90H, why wouldn’t you just do that? </p><p>I think I would buy the JBL Bar 1300X MK2 over this model due to the more expansive sound and room calibration tools, but the Bluesound Pulse Cinema is still a great option if you want to build a multi-room smart sound system. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/soundbars/bluesound-pulse-cinema-review</link>
                                                                            <description>
                            <![CDATA[ The Bluesound Pulse Cinema is a pretty pricey all-in-one soundbar, but has great bass, great music performance, and great buildability. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">qvAyTscrMkYcWrqAhTnja</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/GnLFxHeLq6bkj2QRVrsmif-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 13 Aug 2026 12:40:55 +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.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/GnLFxHeLq6bkj2QRVrsmif-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[the bluesound pulse cinema soundbar photographed with a TV and the tom&#039;s guide blue background]]></media:description>                                                            <media:text><![CDATA[the bluesound pulse cinema soundbar photographed with a TV and the tom&#039;s guide blue background]]></media:text>
                                <media:title type="plain"><![CDATA[the bluesound pulse cinema soundbar photographed with a TV and the tom&#039;s guide blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/GnLFxHeLq6bkj2QRVrsmif-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Bluesound Pulse Cinema is an incredibly interesting soundbar. It’s $1,499, which puts it firmly in the “premium” category, and it’s all-in-one, which means it has two subwoofer channels in the soundbar itself. So, is all of this enough to make it one of the <a href="https://www.tomsguide.com/us/best-soundbar-speakers,review-2004.html">best soundbars</a> available in 2026? </p><p>Unfortunately, not quite. While the overall sound performance is great, a few dialogue issues (and no dialogue mode) prevented it from achieving full marks. Even so, I didn’t have to strain to understand 99% of dialogue — but those rare instances marred my experience. It’s pretty pricey for just a soundbar, and there’s not as much height as I would expect for $1,500. </p><p>It’s still a worthy contender, though, especially if you want a soundbar as part of a wider multi-speaker sound system. Want to find out more? Keep reading this Bluesound Pulse Cinema review. </p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-specs"><span>Bluesound Pulse Cinema 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/Bluesound-Multi-Room-Streaming-Soundbar-Subwoofer/dp/B0FRV83WY2" target="_blank" rel="nofollow">$1,499</a> / <a href="https://www.richersounds.com/bluesound-pulse-cinema/" target="_blank" rel="nofollow">£1,249</a></p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>HDMI, optical, AirPlay, Bluetooth, RCA, USB-A</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>14.8 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>47.1 x 2.9 x 5.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Gray</p></td></tr><tr><td class="firstcol " ><p><strong>Subwoofer</strong></p></td><td  ><p>No, but sub out</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>3.2.2</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-price-availability"><span>Bluesound Pulse Cinema 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="miPq6L2SUXg5PtcXDWo6Yf" name="Bluesound_PulseCinema_soundbar 3.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/miPq6L2SUXg5PtcXDWo6Yf.jpg" 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 Bluesound Pulse Cinema is a pretty pricey soundbar, at <a href="https://www.amazon.com/Bluesound-Multi-Room-Streaming-Soundbar-Subwoofer/dp/B0FRV83WY2" target="_blank" rel="nofollow">$1,499 from Amazon U.S.</a> and <a href="https://www.richersounds.com/bluesound-pulse-cinema/" target="_blank" rel="nofollow">£1,249 from Richer Sounds in the U.K.</a></p><p>For a 3.2.2-channel soundbar, this is quite expensive. I’ve tested the 23-driver, 7.1.4-channel <a href="https://www.tomsguide.com/audio/soundbars/jbl-bar-1300xmk2-review">JBL Bar 1300X MKII</a>, which is only $300 pricier, at $1,699. I’ve also reviewed the <a href="https://www.tomsguide.com/audio/soundbars/samsung-qs90h-review">Samsung QS90H</a> ($899), which is fantastic if you want excellent bass without needing a separate subwoofer. </p><p>If money is no object, I’d recommend checking out the $2,999 <a href="https://www.tomsguide.com/audio/soundbars/yamaha-true-x-surround-90a-review">Yamaha True X Surround 90A</a>, which is the most immersive Dolby Atmos soundbar I’ve ever used. There’s also the fantastic <a href="https://www.tomsguide.com/audio/soundbars/kef-xio-review">KEF XIO</a>, $1,699, which has incredible sound quality and 360° audio. </p><p>At the cheaper end of the spectrum, I’d recommend the $399 <a href="https://www.tomsguide.com/audio/soundbars/ive-officially-found-my-new-favorite-mid-range-soundbar-it-made-me-feel-like-i-was-at-the-movies">Samsung Q600F</a>/H or the $699 <a href="https://www.tomsguide.com/audio/soundbars/denon-home-sound-bar-550-review">Denon Home 550</a> for soundbars with clarity and depth. </p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-design"><span>Bluesound Pulse Cinema 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="36QZoUEqMWhdH2ffbCiAqf" name="Bluesound_PulseCinema_soundbar 5.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/36QZoUEqMWhdH2ffbCiAqf.jpg" 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 Bluesound Pulse Cinema looks like any other soundbar. It’s pretty plain, but in a purposeful way that means it won’t steal attention away from the rest of your living room. Of course, there’s a mesh exterior and an on-bar button control pad. This illuminates red, green, and blue. </p><p>It’s quite long, at 47 inches, so I’d recommend it only for 55-inch TVs and up. Any smaller would look strange. If you’ve a smaller TV, you might be better suited to the 27-inch <a href="https://www.tomsguide.com/audio/soundbars/bose-smart-soundbar-review">Bose Smart Soundbar </a>(2024), or the 26-inch Denon Home 550.</p><p>The soundbar was much lighter than I thought it would be: 14.8 pounds. Compared to the Yamaha True X Surround 90A’s 24-pound heft and the KEF XIO’s 23-pound bulk, this was noticeably easier to move around. You might not even need a friend to help you set it up. </p><h2 id="controls-11">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="JXhBKBCwxd5U2ny6HLXyGf" name="Bluesound_PulseCinema_soundbar 8.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/JXhBKBCwxd5U2ny6HLXyGf.jpg" 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>There’s no remote, so all your controlling is done via the BlueOS app. I found setting this up relatively straightforward. I had to restart it a couple of times, but it worked without any headache from me. I didn’t even have to set up my Wi-Fi network: the app took the information from AirPlay, which was great. No scrabbling around trying to find that pesky piece of paper taped to the back of my router. </p><p>Ideally, there would be a remote, too. What if your phone breaks and then you’re stuck without a soundbar remote? While this likely won’t be an issue 99% of the time, there’s always that 1%. </p><p>As I connected the soundbar via HDMI eARC, I was able to control it via my TV and Blu-ray player remotes, so I didn’t have any issues here. Specific adjustments like treble and bass volume controls were easy to make in the app. </p><h2 id="features-2">Features</h2><p>I was disappointed to see no dialogue mode or dialogue enhancer. I personally wouldn’t buy a soundbar without this, purely because I like the reassurance of being able to enhance my dialogue if required. </p><p>The Pulse Cinema does have “night”, “movie”, and “music” modes. There’s also a “surround upmixer”, “virtualizer”, and “volume leveler”, alongside treble and bass EQ adjustment. The surround upmixer does what it says on the tin — creates a virtual surround sound environment. “Virtualizer” does pretty much the same thing. I didn’t notice “volume leveler” making much of a change, but it’s supposed to make all the soundbar output the same volume. This would be good if you listen to poorly mastered media or if you’re struggling to hear dialogue.</p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-app"><span>Bluesound Pulse Cinema review: App</span></h2><p>The Bluesound Pulse Cinema uses the BlueOS app, which works almost identically to the Denon HEOS app. This is more of a multi-room smart speaker app than the soundbar-specific JBL One app. There’s no room calibration or individual channel adjustment, which is a shame for a $1,499 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:1600px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7WBQvMTcmbVJK3XgpeHoVL" name="bluesound pulse cinema screenshots" alt="three screenshots from the bluesound OS app showing connectivity functions with the pulse cinema soundbar" src="https://cdn.mos.cms.futurecdn.net/7WBQvMTcmbVJK3XgpeHoVL.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future / Bluesound)</span></figcaption></figure><p>As you can see, the app has TuneIn and integrates music streaming services like TIDAL and Qobuz, making it ideal for building a general multi-speaker home setup. It’s easy to use and easy to set up, so I have no criticisms here. I just wish it had some more soundbar/home cinema-specific modes.</p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-sound-quality"><span>Bluesound Pulse Cinema review: Sound quality</span></h2><p>I test every soundbar in the same room, with “Top Gun: Maverick” on 4K Blu-ray, various other Blu-rays, TV streaming, and music on <a href="https://www.whathifi.com/qobuz/review">Qobuz</a> via AirPlay.</p><h2 id="movies-2">Movies</h2><p>As always, I started with “Top Gun: Maverick”. The opening scene is a goldmine for soundbar testing: high-frequency in-scene sound effects like whirring engines and whooshing ropes, bass-heavy “Danger Zone”, the roaring of jet engines flying overhead. </p><p>I could feel the planes flying around me, but I couldn’t hear them tracking over me. This is a similar issue on Samsung soundbars, namely the QS90H and the Q600F/H. I want height <em>and</em> width! I tested the Yamaha True Sound 90A in the same room, and it offered much more height, which is why it’s the best Dolby Atmos soundbar I’ve ever used — but it <em>is</em> $3,000. I’d also recommend the JBL Bar 1300X MKII as one of the best surround sound experiences — it had plenty of height in the same test, and is only $300 more than the Bluesound. </p><p>Whooshing ropes were clear, and the grinding of Tom Cruise’s wrench was audible in the following scene. The string-heavy soundtrack radiated through the scene like sunlight streaming through cloud cover — but I didn’t get as much width or depth as I wanted. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pGjZSaKJHq3BXkVU2Z7Mjf" name="Bluesound_PulseCinema_soundbar 7.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/pGjZSaKJHq3BXkVU2Z7Mjf.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also noticed some roughness around dialogue, as if the soundbar was struggling to differentiate the bass in Tom Cruise’s voice from the bass in the OST. This was also an issue in “How to Train Your Dragon (2025)”, but I’ll discuss that in detail below. </p><p>However, during action scenes, the bass performance was good. In the first flying scene — Maverick attempting Mach 10 — I could feel the cabin rattling around, the rumbling of the engine right before the explosion. I <em>did</em> want a little more during the explosion, though. </p><p>In the first dogfighting scene, the radioed dialogue was clear, despite the lack of dialogue enhancement mode. The rumbling bass of the jet engines didn’t overpower dialogue. I never struggled to make out speech in this scene. </p><p>I also watched “How to Train Your Dragon (2025)”. Hiccup’s voice-over and the OST were both clear in the opening action scene, but the dragon sound effects were the true standouts here, especially in the grove scene halfway through the movie. When Toothless flew away, the sound swarmed from around me.  </p><p>However, there were unpleasant crackling sounds when the characters were talking and moving. While this might be a “How to Train Your Dragon (2025)” mixing issue, I also experienced some crackling during Tom Cruise’s dialogue in “Top Gun: Maverick”. Amazon reviews mention “bass popping”, but I didn’t experience anything like this.</p><p>I also watched the original Disney “Alice in Wonderland”. This movie is encoded with Dolby Digital rather than true Atmos. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="y7Lp5gU9dysobcDZJsGd4f" name="Bluesound_PulseCinema_soundbar 9.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/y7Lp5gU9dysobcDZJsGd4f.jpg" 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>“Alice in Wonderland” sounded fantastic. The soundbar had no issues switching from music to dialogue to ‘action’ scenes (Alice falling down the rabbit hole). When Alice fell, my stomach actually dropped thanks to the sound effects. The flutes sounded particularly excellent, and I never struggled to hear dialogue — but this isn’t exactly a blockbuster action movie. </p><p>As a final blockbuster test, I watched “The Batman (2022)”. The eerie, tense, string-heavy OST didn’t overpower the dialogue or in-scene TV sounds whilst screeching in the background of the scene. The falling rain was exceptionally wide; I could hear it as if it were actually pouring in the testing room. </p><p>The iconic “Something in the Way” scene was my favorite scene of the movie. I could hear Pattinson’s brooding voiceover clearly, despite the music and motorcycle sound effects. I could also make out the newsreader speech and the rewinding sound effects over the music. This performance was really great — much better than “How to Train Your Dragon (2025)”.</p><h2 id="tv-2">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="WS2qA9Re9XxDXAvVTovBjf" name="Bluesound_PulseCinema_soundbar 6.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/WS2qA9Re9XxDXAvVTovBjf.jpg" 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 playback, I watched “Interview with the Vampire” on basic Netflix. I didn’t notice any crackling or dialogue hissing here. The saloon-inspired soundtrack danced across the soundstage, but never overpowered the dialogue. In-scene sound effects like crickets and crackling fires were clear and pronounced, but layered perfectly beneath dialogue. </p><p>I noticed some compression issues when the scenes switched between voiceover and retrospective scenes. “Interview with the Vampire” is that — an interview, taking place in modern day. The protagonist’s dialogue weaves from “present day” interview answers into the retrospective scenes of which he speaks. During each scene, I didn’t notice any processing issues, but during transitional scenes, some dialogue was compressed. </p><p>Overall, though, I enjoyed this experience. I never struggled to hear dialogue or make out high-frequency sound effects.</p><h2 id="music-2">Music</h2><p>To test music playback, I listened to a range of genres via Qobuz, our <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service for audiophiles</a>. ‘Von dutch (A.G. Cook remix ft. Addison Rae)’ by Charli xcx is an electronic track with processed synths and rolling bass. Right at the end of the track, there’s a spoken section. This section sounded <em>fantastic</em>. I felt like the words were bouncing around the room. </p><p>After, I played ‘I Was Neon’, an indie track with rounded female vocals and slightly crispy guitars. The percussion was a little muted, but not overly so, and the licks of electric guitar after the chorus were particularly melodic. While I wanted a little more control and complexity to the bass performance, at the end of the day, this is a soundbar, not a speaker. This music performance is impressive. </p><p>Lastly, I switched to heavier rock with ‘My God Is The Sun’ by Queens of the Stone Age. As with the Julia Jacklin track, the overall performance was good for a soundbar. Homme’s vocals were clear and propulsive, and the percussion wasn’t tinny. However, I wanted <em>more</em>. More bass, more complexity, more passion. At the end of the day, though, this is a soundbar. And soundbars will never be as impressive as genuine music-focused speakers.</p><p>Overall, for music, this is one of the best soundbars I’ve ever used. It matches the Bose Smart Soundbar (2024) and exceeds the Samsung Q600H and QS90H. </p><h2 class="article-body__section" id="section-bluesound-pulse-cinema-review-verdict"><span>Bluesound Pulse Cinema 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="G9MedBLyu7tp94V44j3HNf" name="Bluesound_PulseCinema_soundbar 10.JPG" alt="the bluesound pulse cinema soundbar photographed with a TV and the tom's guide blue background" src="https://cdn.mos.cms.futurecdn.net/G9MedBLyu7tp94V44j3HNf.jpg" 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 Bluesound Pulse Cinema is a great soundbar, with impressive width and perhaps the best music on a soundbar I’ve ever experienced. The app is easy to use and straightforward, with no connectivity or setup issues. </p><p>Even so, for it to achieve our coveted 4-star rating, it would need a few changes: no dialogue/bass rumbling issues (as experienced during “Top Gun: Maverick” and “How to Train Your Dragon (2025)”, and more premium soundbar-specific features — or a lower price. When you can spend $300 extra for the near-perfect JBL Bar 1300X MK2, or save $600 and get the all-in-one Samsung QS90H, why wouldn’t you just do that? </p><p>I think I would buy the JBL Bar 1300X MK2 over this model due to the more expansive sound and room calibration tools, but the Bluesound Pulse Cinema is still a great option if you want to build a multi-room smart sound system. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested Nothing’s new earbuds — they sound great but have a huge privacy problem ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The Nothing Ear (3a) are the latest mid-range earbuds from budget British brand Nothing. The new buds sit underneath the flagship Nothing Ear (3), but there’s really very little between the two. </p><p>For $99, the Nothing Ear (3a) are a bit of a bargain. They offer striking design, plenty of features and decent sound quality (after some fettling in the EQ). If you’re only considering their core performance, these are some of the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> around for people on a budget.</p><p>However, I have one major concern around some new onboard recording features. In short, these earbuds will let you secretly record and store phone calls. So… spy buds. Is this a bug or a feature?</p><p>Want to know more? Keep reading my full Nothing Ear (3a) review to find out.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-specs"><span>Nothing Ear (3a) 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/gp/aw/d/B0GZW668V4/" target="_blank" rel="nofollow">$99</a> / <a href="https://www.amazon.co.uk/gp/aw/d/B0GZW668V4/" target="_blank" rel="nofollow">£99</a></p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 6.0</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP54</p></td></tr><tr><td class="firstcol " ><p><strong>Battery</strong></p></td><td  ><p>10 hours (buds) 42 hours (buds + case)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>0.15 ounces (per bud)</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>1.2 x 0.9 x 0.9 inches (per bud)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Pink; Yellow; White; Black</p></td></tr><tr><td class="firstcol " ><p><strong>Compatibility</strong></p></td><td  ><p>iOS, Android</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-nothing-ear-3a-review-price-availability"><span>Nothing Ear (3a) 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="rAidZXZXg37YBEVGLWHQMd" name="Nothing Ear (3a)_Buds-9.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/rAidZXZXg37YBEVGLWHQMd.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Nothing Ear (3a) cost just <a href="https://www.amazon.com/gp/aw/d/B0GZW668V4/" target="_blank" rel="nofollow">$99</a> / <a href="https://www.amazon.co.uk/gp/aw/d/B0GZW668V4/" target="_blank" rel="nofollow">£99</a>, undercutting the brand’s flagship <a href="https://www.tomsguide.com/audio/earbuds/nothing-ear-3-review">Nothing Ear (3)</a> ($149), as well as big-name rivals like the <a href="https://www.tomsguide.com/audio/earbuds/apple-airpods-4-review">Apple AirPods 4 ANC</a> ($179) and <a href="https://www.tomsguide.com/audio/earbuds/samsung-galaxy-buds-4-review">Samsung Galaxy Buds 4</a> ($179). If you’re using either an iPhone or Galaxy phone, you’ll benefit more from the ecosystem integration of the latter two earbuds — the ability to swap seamlessly between an iPhone and iPad, for example. Otherwise, it’s difficult to recommend the big names over the Ear (3a), given the $80 difference. </p><p>At this price, the Ear (3a) are in direct competition with other budget-ish heroes like the <a href="https://www.tomsguide.com/audio/earbuds/earfun-air-pro-4-plus-review">EarFun Air Pro 4+</a> ($99) and <a href="https://www.tomsguide.com/audio/earbuds/cmf-buds-2-plus-review">CMF Buds 2 Plus</a> ($79). The Air Pro 4+ are the toughest to beat, offering equally as powerful ANC and excellent sound. That said, the Air Pro 4+ <em>look</em> cheap and don’t offer the same number of features as the stacked Ear (3a).</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-design"><span>Nothing Ear (3a) review: Design</span></h2><p>Nothing is known for its striking design ethos. True to form, the Ear (3a) look phenomenal. Bold colors and Nothing’s trademark see-through design give pure cyberpunk vibes. I tested the hot pink variant, but there’s an equally striking yellow model, plus Nothing’s typical white or black colorways.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1063px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Jk2EKKHSxfmT6hqGPoxuSd" name="Nothing Ear (3a)_Buds-10.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/v2/t:187,l:429,cw:1063,ch:598,q:80/Jk2EKKHSxfmT6hqGPoxuSd.jpg" 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>Build quality is excellent, with both case and buds feeling high quality. The case lid is magnetic and snaps closed reassuringly, while the buds click magnetically into place with similar confidence. The case is transparent, which looks scruffy pretty quickly, but I’ve yet to see a case that doesn’t reach that point eventually.</p><p>The flagship <a href="https://www.tomsguide.com/audio/earbuds/nothing-ear-3-review">Nothing Ear (3)</a> have a more premium case, with wireless charging and a microphone built into it. They also have a bone-conduction mic in the earbuds. But I wouldn’t say there’s enough there to justify an extra $50 on the Ear (3). They use the same 12mm drivers, while the Ear (3a) benefit from the new stem controls and the recording features I’ll cover later.</p><p>The Ear (3a) buds and case are each IP54 rated, just like the <a href="https://www.tomsguide.com/audio/earbuds/apple-airpods-4-review">Apple AirPods 4</a>, meaning they’ll stand up to rain if you’re caught in a shower, and you can use them for working out. The EarFun AirPro 4+ are IP55 rated, so a little tougher against water. </p><p>The CMF Buds 2 Plus have a slightly higher IP55 earbud rating, but only IPX2 for the case. That could be an issue if your clothes got wet with the case in your pocket. I’d rather have IP54 all-round.</p><h3 id="comfort-7">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PCfFDAneCP5fetvdRrhRQd" name="Nothing Ear (3a)_Buds-12.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/PCfFDAneCP5fetvdRrhRQd.jpg" 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>Mileage is always going to vary with earbud comfort, based on your individual ear holes. But I’ve found the Ear (3a) extremely comfortable. The tips are made from a softer, lighter silicone than my daily drivers, the <a href="https://www.tomsguide.com/audio-headphones/earbuds/earfun-air-pro-4-review">EarFun Air Pro 4</a> ($79), and I noticed immediately how much nicer the Nothing tips felt. I wore the buds for two train journeys, plus daily life around the city, with no issues or soreness whatsoever.</p><h3 id="controls-12">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="zK8XfyCSRPNveocBHndhA9" name="Nothing Ear (3a)_Screenshots-5" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/zK8XfyCSRPNveocBHndhA9.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/zK8XfyCSRPNveocBHndhA9.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>I really like the Ear (3a) controls. You pinch either side of the drop stem, which I much prefer to the touch controls we often see on earbuds. The stem gives subtle haptic feedback to confirm you’ve actuated the controls, and they proved reliable and accurate when I used them while walking through central London.</p><p>You can reassign commands in the Nothing X app, with loads of combos to choose from. There are a few too many, to be honest. I simply memorized playback controls and the ANC toggle, which is all I really need.</p><h3 id="connectivity-9">Connectivity</h3><p>The Ear (3a) use the latest Bluetooth 6.0, which is nice to see (and not a given) — even the <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">Apple AirPods Max 2</a> launched in 2026 with the ageing version 5.3. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CD4HYMTt2Uuscmuo68bV5A" name="Nothing Ear (3a)_Screenshots-6" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/CD4HYMTt2Uuscmuo68bV5A.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/CD4HYMTt2Uuscmuo68bV5A.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">I loved using the Nothing X app to view and curate my multipoint connections. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>Bluetooth 6.0 offers higher data transfer rates and more modern codecs than older iterations, reduced power consumption, more stable connection, plus better pinpoint location detection (for ‘Find My’-type services). It’s always nice to see the latest tech in products, but especially budget ones like this.</p><p>Codec-wise, the Ear (3a) support the usual AAC and SBC, plus LDAC for hi-res streaming. There’s no LC3plus, an updated version of the LC3 low-energy codec, which is one of the key codec updates in Bluetooth 6.0 and improves latency. That’s not a huge deal, though.</p><p>The Ear (3a) feature multipoint connectivity, so I hooked up my phone and work laptop simultaneously and switched between the two. The Nothing X app shows you all connected devices with on/off toggles, so you can curate your connections easily.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-sound-quality"><span>Nothing Ear (3a) review: Sound quality</span></h2><p>The Nothing Ear (3a) stock tuning (i.e., how the equalizer is set up by default) needs work. It’s not awful. Treble and higher mids are clear, with lighter percussion sounding pretty crisp and vocals staying prominent. This is actually an improvement on the flagship Ear (3), whose treble we found overly bright and uncomfortable. Similar to the Ear (3) though, the (3a)’s low end is way over-amplified by default, at least for my liking — if you like bass and bass alone, it might be for you. Not me.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1333px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="NrQ2mqeq6HGAs72QTrS6Sd" name="Nothing Ear (3a)_Buds-11.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/v2/t:108,l:315,cw:1333,ch:750,q:80/NrQ2mqeq6HGAs72QTrS6Sd.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>I found heavier and busier tracks difficult to listen to. Mid-range instrument separation effectively disappeared under overwhelming bass. Despite boasting impressive low extension (i.e. how low in the frequency range they can actually reproduce), the drivers can’t handle all the bass amplification EQ’d their way either. In deep electronic music like Burn Water’s <em>Ikigai</em>, larger warbles suffered distortion, as did the sweeping wave of sub bass in the final 20 seconds of SOHN’s <em>Antigravity</em>. </p><p>The low end simply needs controlling. Thankfully, you can retune the buds all you like in the Nothing X app. There are three different EQ modes. A simplistic version; some per-genre presets; plus a full and fantastic 8-band parametric EQ. After a lot of fettling in the parametric EQ, paring back bass in particular, the Ear (3a) sounded way more balanced. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4688PWB9hjRXZBcxQRvQMQ" name="Nothing Ear (3a)_Screenshots-1" alt="Screenshots showing the Nothing Ear (3a) being customized in the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/4688PWB9hjRXZBcxQRvQMQ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>Kick drums and basslines were no longer just boomy, fuzzy messes. Addison Rae’s <em>Fame Is a Gun</em> was no longer just one big kick drum, and I could hear the individual oscillations of the track’s wavy synths. Melvins’ <em>Honey Bucket</em> and The Smashing Pumpkins’ <em>Siva</em>, whose distorted, sludgy guitar riffs were effectively buried by booming before, now sounded fantastic, with a hearty amount of crunch and separation in the mid-range. </p><p>Likewise, with space to breathe, vocals sounded fantastic. I could pick apart each of Blusher’s voices in the harmonies of <em>Your Love Is My Drug, </em>and could hear the soft, slowly fading reverb on Chris Stapleton's vocals in <em>Death Row</em>.</p><p>Sure, it took a little time with EQ to get the Ear (3a) sounding grown up, but for $99, that’s not a big sacrifice to make.</p><h3 id="soundstage">Soundstage</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LGBiNKnkoHmnzfUxEkBCG9" name="Nothing Ear (3a)_Screenshots-4" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/LGBiNKnkoHmnzfUxEkBCG9.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/LGBiNKnkoHmnzfUxEkBCG9.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>The Ear (3a)’s soundstage is pretty decent for a set of ANC earbuds (which are inherently intimate) — <em>Antigravity</em>’s bouncy spatial intro had a convincing amount of width, for instance. There’s a ‘Spatial’ mode in the app, intended to boost three-dimensionality, but it isn’t worth it. It forces the EQ back into ‘Balanced’ and then just makes everything echoey. It’s how I’d imagine music sounds inside a medieval dungeon. </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>, our favorite </em><a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"><em>music streaming service</em></a><em> for audiophiles.</em></p><iframe allow="" height="390" width="400" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://widget.qobuz.com/playlist/59666746?zone=GB-en"></iframe><h2 class="article-body__section" id="section-nothing-ear-3a-review-anc"><span>Nothing Ear (3a) review: ANC</span></h2><p>The Nothing Ear (3a) have three-level ANC (low, medium, high), plus an adaptive mode that changes automatically, and a transparency mode for staying aware of your surroundings. You switch between these via the app or stem controls.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="F3ucgjrQJ459Kkp7j9sXz8" name="Nothing Ear (3a)_Screenshots-3" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/F3ucgjrQJ459Kkp7j9sXz8.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/F3ucgjrQJ459Kkp7j9sXz8.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>Adaptive did a good job at tailoring the ANC to the scenario I was in, and I could hear it switching up to the highest level when I entered the busy Paddington Station in London.</p><p>The ANC itself is okay, too. It’s not the best, with nearby conversations and hubbub making it through during a train journey. However, lower-frequency  sounds like the train’s engine were mostly cut. I also wore the buds while walking around the city, and the ANC cut most traffic noise, too. </p><p>You’ll get better ANC performance on the EarFun Air Pro 4+, but the Ear (3a) still did the job I needed them to do: I was able to work on the train with no distractions.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-app"><span>Nothing Ear (3a) review: App</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xCutNmYzzwFxvVb3ZdnS4Q" name="Nothing Ear (3a)_Screenshots-2" alt="Screenshots showing the Nothing Ear (3a) being customized in the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/xCutNmYzzwFxvVb3ZdnS4Q.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/xCutNmYzzwFxvVb3ZdnS4Q.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>I love the Nothing X app. It’s absolutely packed full of features, and useful for virtually every aspect of operating the buds, from sound (where it’s absolutely necessary) through to call recording, controls, ANC and updates.</p><p>Given the app plays such a pivotal role in using the buds, I’m glad it’s so slick and polished. It hasn’t crashed on me once, and is extremely easy to navigate and use.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-call-quality"><span>Nothing Ear (3a) review: Call quality</span></h2><p>To test call quality, I called my wife while on a walk. With three bars of reception, she said my voice sounded weak and distant, although she understood everything I said. I didn’t sound hi-res, but then again I didn’t need to. The Ear (3a) got the job done. </p><p>Nothing’s flagship Ear (3) have the usual earbuds mics, plus an additional microphone built into the case. Given this is one of the only differences between the two sets of buds, it’s not enough reason to recommend the pricier model.</p><p>The Ear (3a) have some novel call recording features, but I’ll discuss these in the Privacy concerns section below.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-battery-life"><span>Nothing Ear (3a) 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uikVWRTysVBk8NkdGNgQ9d" name="Nothing Ear (3a)_Buds-14.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/uikVWRTysVBk8NkdGNgQ9d.jpg" 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>Nothing claims the Ear (3a) will last up to 10 hours with ANC off, or 6 hours with ANC enabled. This aligns with my testing, with the buds dropping about 30% over a nearly-two-hour train journey. In total, you can expect 42 hours without ANC from the buds and charging case combined, or 25 with ANC. </p><p>This is on the low side, but not awful. The Apple AirPods 4 ANC, for example, manage 24 hours in total with ANC on, while the (Nothing-owned) CMF Buds Pro 2 last 26 hours. The CMF Buds 2 Plus offer 33 hours, while the EarFun Air Pro 4+ can go for 36 hours.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-privacy-concerns"><span>Nothing Ear (3a) review: Privacy concerns</span></h2><p>I wouldn’t normally need to write a section on privacy — and the fact that I have should raise alarm bells. The Ear (3a) have a range of recording features, which look fairly innocent on the surface. There’s an Audio Snapshot mode, which will store the last minute of media playback — i.e. your favorite excerpts from music or 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.25%;"><img id="AKaoFXC3XZh5NsWvnrdu4A" name="Nothing Ear (3a)_Screenshots-7" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/AKaoFXC3XZh5NsWvnrdu4A.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/AKaoFXC3XZh5NsWvnrdu4A.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>They can also record and store two hours of calls or voice notes, with a transcription service available via a subscription in the Nothing X app. Very handy for any students or journalists needing to record and transcribe online meetings. But I have serious privacy concerns with this feature. </p><p>During test calls, initiating ‘call recording’ delivered a prompt to both parties saying “this call will be recorded in 3, 2, 1.” Fine — good, even! Not so fast. After hitting mute on my side, the other party heard no prompt whatsoever. I ran the test with my colleague Nikita, then again with my wife. Same result for both. So, the Ear (3a) can record calls without the other person knowing or consenting. Here in the U.K., that isn’t illegal, but in many U.S. states it is. Regardless, it’s immoral unless under specific public interest needs.</p><p>I don’t like the idea of this feature becoming widespread in earbuds, either. Sure, there are already plenty of ways to record calls nefariously. But adding this kind of thing to tech as ubiquitous as earbuds feels like it’ll make non-consensual recording much easier and more commonplace.</p><p>I’ve reached out to Nothing for comment on this issue, but it hasn't got back to me yet.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-verdict"><span>Nothing Ear (3a) 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="hvhmuMmVooSD2tf9zDBhuc" name="Nothing Ear (3a)_Buds-4.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/hvhmuMmVooSD2tf9zDBhuc.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Nothing Ear (3a) surprised me. I thought <em>nothing</em> could tempt me away from my long-term daily drivers, the EarFun Air Pro 4. But it seems <em>Nothing</em> can!</p><p>I really, really wish they didn’t make it so easy to spy on people, but, putting aside the privacy stuff just for a minute and judging these on their merits as earbuds, the Ear (3a) offer impressive performance for their price. Consider the striking design, premium build quality and decent comfort, and these buds start looking like a proper bargain — in fact, I’m not sure why you’d buy the pricier Nothing 3 flagship. </p><p>OK, the sound needs some work by default, but the drivers are inherently very good, and it only takes a little time in the EQ to sort everything out. For under $100, that’s time I’m more than happy to spend.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/nothing-ear-3a-review</link>
                                                                            <description>
                            <![CDATA[ The Ear (3a) perform pretty well, with a little EQ fettling. I have a pretty big privacy concern with them though. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">JsnwMKFxVhzjixgjeEVaV3</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/GxFAcfXkhNZHJs8cvyerSd-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 13 Aug 2026 12:38:51 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Earbuds]]></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.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/GxFAcfXkhNZHJs8cvyerSd-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Nothing Earbuds (3a) in pink, on a wooden surface]]></media:description>                                                            <media:text><![CDATA[The Nothing Earbuds (3a) in pink, on a wooden surface]]></media:text>
                                <media:title type="plain"><![CDATA[The Nothing Earbuds (3a) in pink, on a wooden surface]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/GxFAcfXkhNZHJs8cvyerSd-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Nothing Ear (3a) are the latest mid-range earbuds from budget British brand Nothing. The new buds sit underneath the flagship Nothing Ear (3), but there’s really very little between the two. </p><p>For $99, the Nothing Ear (3a) are a bit of a bargain. They offer striking design, plenty of features and decent sound quality (after some fettling in the EQ). If you’re only considering their core performance, these are some of the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> around for people on a budget.</p><p>However, I have one major concern around some new onboard recording features. In short, these earbuds will let you secretly record and store phone calls. So… spy buds. Is this a bug or a feature?</p><p>Want to know more? Keep reading my full Nothing Ear (3a) review to find out.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-specs"><span>Nothing Ear (3a) 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/gp/aw/d/B0GZW668V4/" target="_blank" rel="nofollow">$99</a> / <a href="https://www.amazon.co.uk/gp/aw/d/B0GZW668V4/" target="_blank" rel="nofollow">£99</a></p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 6.0</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IP54</p></td></tr><tr><td class="firstcol " ><p><strong>Battery</strong></p></td><td  ><p>10 hours (buds) 42 hours (buds + case)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>0.15 ounces (per bud)</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>1.2 x 0.9 x 0.9 inches (per bud)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Pink; Yellow; White; Black</p></td></tr><tr><td class="firstcol " ><p><strong>Compatibility</strong></p></td><td  ><p>iOS, Android</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-nothing-ear-3a-review-price-availability"><span>Nothing Ear (3a) 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="rAidZXZXg37YBEVGLWHQMd" name="Nothing Ear (3a)_Buds-9.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/rAidZXZXg37YBEVGLWHQMd.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Nothing Ear (3a) cost just <a href="https://www.amazon.com/gp/aw/d/B0GZW668V4/" target="_blank" rel="nofollow">$99</a> / <a href="https://www.amazon.co.uk/gp/aw/d/B0GZW668V4/" target="_blank" rel="nofollow">£99</a>, undercutting the brand’s flagship <a href="https://www.tomsguide.com/audio/earbuds/nothing-ear-3-review">Nothing Ear (3)</a> ($149), as well as big-name rivals like the <a href="https://www.tomsguide.com/audio/earbuds/apple-airpods-4-review">Apple AirPods 4 ANC</a> ($179) and <a href="https://www.tomsguide.com/audio/earbuds/samsung-galaxy-buds-4-review">Samsung Galaxy Buds 4</a> ($179). If you’re using either an iPhone or Galaxy phone, you’ll benefit more from the ecosystem integration of the latter two earbuds — the ability to swap seamlessly between an iPhone and iPad, for example. Otherwise, it’s difficult to recommend the big names over the Ear (3a), given the $80 difference. </p><p>At this price, the Ear (3a) are in direct competition with other budget-ish heroes like the <a href="https://www.tomsguide.com/audio/earbuds/earfun-air-pro-4-plus-review">EarFun Air Pro 4+</a> ($99) and <a href="https://www.tomsguide.com/audio/earbuds/cmf-buds-2-plus-review">CMF Buds 2 Plus</a> ($79). The Air Pro 4+ are the toughest to beat, offering equally as powerful ANC and excellent sound. That said, the Air Pro 4+ <em>look</em> cheap and don’t offer the same number of features as the stacked Ear (3a).</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-design"><span>Nothing Ear (3a) review: Design</span></h2><p>Nothing is known for its striking design ethos. True to form, the Ear (3a) look phenomenal. Bold colors and Nothing’s trademark see-through design give pure cyberpunk vibes. I tested the hot pink variant, but there’s an equally striking yellow model, plus Nothing’s typical white or black colorways.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1063px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Jk2EKKHSxfmT6hqGPoxuSd" name="Nothing Ear (3a)_Buds-10.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/v2/t:187,l:429,cw:1063,ch:598,q:80/Jk2EKKHSxfmT6hqGPoxuSd.jpg" 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>Build quality is excellent, with both case and buds feeling high quality. The case lid is magnetic and snaps closed reassuringly, while the buds click magnetically into place with similar confidence. The case is transparent, which looks scruffy pretty quickly, but I’ve yet to see a case that doesn’t reach that point eventually.</p><p>The flagship <a href="https://www.tomsguide.com/audio/earbuds/nothing-ear-3-review">Nothing Ear (3)</a> have a more premium case, with wireless charging and a microphone built into it. They also have a bone-conduction mic in the earbuds. But I wouldn’t say there’s enough there to justify an extra $50 on the Ear (3). They use the same 12mm drivers, while the Ear (3a) benefit from the new stem controls and the recording features I’ll cover later.</p><p>The Ear (3a) buds and case are each IP54 rated, just like the <a href="https://www.tomsguide.com/audio/earbuds/apple-airpods-4-review">Apple AirPods 4</a>, meaning they’ll stand up to rain if you’re caught in a shower, and you can use them for working out. The EarFun AirPro 4+ are IP55 rated, so a little tougher against water. </p><p>The CMF Buds 2 Plus have a slightly higher IP55 earbud rating, but only IPX2 for the case. That could be an issue if your clothes got wet with the case in your pocket. I’d rather have IP54 all-round.</p><h3 id="comfort-7">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PCfFDAneCP5fetvdRrhRQd" name="Nothing Ear (3a)_Buds-12.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/PCfFDAneCP5fetvdRrhRQd.jpg" 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>Mileage is always going to vary with earbud comfort, based on your individual ear holes. But I’ve found the Ear (3a) extremely comfortable. The tips are made from a softer, lighter silicone than my daily drivers, the <a href="https://www.tomsguide.com/audio-headphones/earbuds/earfun-air-pro-4-review">EarFun Air Pro 4</a> ($79), and I noticed immediately how much nicer the Nothing tips felt. I wore the buds for two train journeys, plus daily life around the city, with no issues or soreness whatsoever.</p><h3 id="controls-12">Controls</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="zK8XfyCSRPNveocBHndhA9" name="Nothing Ear (3a)_Screenshots-5" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/zK8XfyCSRPNveocBHndhA9.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/zK8XfyCSRPNveocBHndhA9.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>I really like the Ear (3a) controls. You pinch either side of the drop stem, which I much prefer to the touch controls we often see on earbuds. The stem gives subtle haptic feedback to confirm you’ve actuated the controls, and they proved reliable and accurate when I used them while walking through central London.</p><p>You can reassign commands in the Nothing X app, with loads of combos to choose from. There are a few too many, to be honest. I simply memorized playback controls and the ANC toggle, which is all I really need.</p><h3 id="connectivity-9">Connectivity</h3><p>The Ear (3a) use the latest Bluetooth 6.0, which is nice to see (and not a given) — even the <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">Apple AirPods Max 2</a> launched in 2026 with the ageing version 5.3. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="CD4HYMTt2Uuscmuo68bV5A" name="Nothing Ear (3a)_Screenshots-6" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/CD4HYMTt2Uuscmuo68bV5A.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/CD4HYMTt2Uuscmuo68bV5A.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">I loved using the Nothing X app to view and curate my multipoint connections. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>Bluetooth 6.0 offers higher data transfer rates and more modern codecs than older iterations, reduced power consumption, more stable connection, plus better pinpoint location detection (for ‘Find My’-type services). It’s always nice to see the latest tech in products, but especially budget ones like this.</p><p>Codec-wise, the Ear (3a) support the usual AAC and SBC, plus LDAC for hi-res streaming. There’s no LC3plus, an updated version of the LC3 low-energy codec, which is one of the key codec updates in Bluetooth 6.0 and improves latency. That’s not a huge deal, though.</p><p>The Ear (3a) feature multipoint connectivity, so I hooked up my phone and work laptop simultaneously and switched between the two. The Nothing X app shows you all connected devices with on/off toggles, so you can curate your connections easily.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-sound-quality"><span>Nothing Ear (3a) review: Sound quality</span></h2><p>The Nothing Ear (3a) stock tuning (i.e., how the equalizer is set up by default) needs work. It’s not awful. Treble and higher mids are clear, with lighter percussion sounding pretty crisp and vocals staying prominent. This is actually an improvement on the flagship Ear (3), whose treble we found overly bright and uncomfortable. Similar to the Ear (3) though, the (3a)’s low end is way over-amplified by default, at least for my liking — if you like bass and bass alone, it might be for you. Not me.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1333px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="NrQ2mqeq6HGAs72QTrS6Sd" name="Nothing Ear (3a)_Buds-11.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/v2/t:108,l:315,cw:1333,ch:750,q:80/NrQ2mqeq6HGAs72QTrS6Sd.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>I found heavier and busier tracks difficult to listen to. Mid-range instrument separation effectively disappeared under overwhelming bass. Despite boasting impressive low extension (i.e. how low in the frequency range they can actually reproduce), the drivers can’t handle all the bass amplification EQ’d their way either. In deep electronic music like Burn Water’s <em>Ikigai</em>, larger warbles suffered distortion, as did the sweeping wave of sub bass in the final 20 seconds of SOHN’s <em>Antigravity</em>. </p><p>The low end simply needs controlling. Thankfully, you can retune the buds all you like in the Nothing X app. There are three different EQ modes. A simplistic version; some per-genre presets; plus a full and fantastic 8-band parametric EQ. After a lot of fettling in the parametric EQ, paring back bass in particular, the Ear (3a) sounded way more balanced. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4688PWB9hjRXZBcxQRvQMQ" name="Nothing Ear (3a)_Screenshots-1" alt="Screenshots showing the Nothing Ear (3a) being customized in the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/4688PWB9hjRXZBcxQRvQMQ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>Kick drums and basslines were no longer just boomy, fuzzy messes. Addison Rae’s <em>Fame Is a Gun</em> was no longer just one big kick drum, and I could hear the individual oscillations of the track’s wavy synths. Melvins’ <em>Honey Bucket</em> and The Smashing Pumpkins’ <em>Siva</em>, whose distorted, sludgy guitar riffs were effectively buried by booming before, now sounded fantastic, with a hearty amount of crunch and separation in the mid-range. </p><p>Likewise, with space to breathe, vocals sounded fantastic. I could pick apart each of Blusher’s voices in the harmonies of <em>Your Love Is My Drug, </em>and could hear the soft, slowly fading reverb on Chris Stapleton's vocals in <em>Death Row</em>.</p><p>Sure, it took a little time with EQ to get the Ear (3a) sounding grown up, but for $99, that’s not a big sacrifice to make.</p><h3 id="soundstage">Soundstage</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LGBiNKnkoHmnzfUxEkBCG9" name="Nothing Ear (3a)_Screenshots-4" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/LGBiNKnkoHmnzfUxEkBCG9.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/LGBiNKnkoHmnzfUxEkBCG9.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>The Ear (3a)’s soundstage is pretty decent for a set of ANC earbuds (which are inherently intimate) — <em>Antigravity</em>’s bouncy spatial intro had a convincing amount of width, for instance. There’s a ‘Spatial’ mode in the app, intended to boost three-dimensionality, but it isn’t worth it. It forces the EQ back into ‘Balanced’ and then just makes everything echoey. It’s how I’d imagine music sounds inside a medieval dungeon. </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>, our favorite </em><a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services"><em>music streaming service</em></a><em> for audiophiles.</em></p><iframe allow="" height="390" width="400" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://widget.qobuz.com/playlist/59666746?zone=GB-en"></iframe><h2 class="article-body__section" id="section-nothing-ear-3a-review-anc"><span>Nothing Ear (3a) review: ANC</span></h2><p>The Nothing Ear (3a) have three-level ANC (low, medium, high), plus an adaptive mode that changes automatically, and a transparency mode for staying aware of your surroundings. You switch between these via the app or stem controls.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="F3ucgjrQJ459Kkp7j9sXz8" name="Nothing Ear (3a)_Screenshots-3" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/F3ucgjrQJ459Kkp7j9sXz8.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/F3ucgjrQJ459Kkp7j9sXz8.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>Adaptive did a good job at tailoring the ANC to the scenario I was in, and I could hear it switching up to the highest level when I entered the busy Paddington Station in London.</p><p>The ANC itself is okay, too. It’s not the best, with nearby conversations and hubbub making it through during a train journey. However, lower-frequency  sounds like the train’s engine were mostly cut. I also wore the buds while walking around the city, and the ANC cut most traffic noise, too. </p><p>You’ll get better ANC performance on the EarFun Air Pro 4+, but the Ear (3a) still did the job I needed them to do: I was able to work on the train with no distractions.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-app"><span>Nothing Ear (3a) review: App</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xCutNmYzzwFxvVb3ZdnS4Q" name="Nothing Ear (3a)_Screenshots-2" alt="Screenshots showing the Nothing Ear (3a) being customized in the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/xCutNmYzzwFxvVb3ZdnS4Q.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/xCutNmYzzwFxvVb3ZdnS4Q.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>I love the Nothing X app. It’s absolutely packed full of features, and useful for virtually every aspect of operating the buds, from sound (where it’s absolutely necessary) through to call recording, controls, ANC and updates.</p><p>Given the app plays such a pivotal role in using the buds, I’m glad it’s so slick and polished. It hasn’t crashed on me once, and is extremely easy to navigate and use.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-call-quality"><span>Nothing Ear (3a) review: Call quality</span></h2><p>To test call quality, I called my wife while on a walk. With three bars of reception, she said my voice sounded weak and distant, although she understood everything I said. I didn’t sound hi-res, but then again I didn’t need to. The Ear (3a) got the job done. </p><p>Nothing’s flagship Ear (3) have the usual earbuds mics, plus an additional microphone built into the case. Given this is one of the only differences between the two sets of buds, it’s not enough reason to recommend the pricier model.</p><p>The Ear (3a) have some novel call recording features, but I’ll discuss these in the Privacy concerns section below.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-battery-life"><span>Nothing Ear (3a) 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uikVWRTysVBk8NkdGNgQ9d" name="Nothing Ear (3a)_Buds-14.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/uikVWRTysVBk8NkdGNgQ9d.jpg" 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>Nothing claims the Ear (3a) will last up to 10 hours with ANC off, or 6 hours with ANC enabled. This aligns with my testing, with the buds dropping about 30% over a nearly-two-hour train journey. In total, you can expect 42 hours without ANC from the buds and charging case combined, or 25 with ANC. </p><p>This is on the low side, but not awful. The Apple AirPods 4 ANC, for example, manage 24 hours in total with ANC on, while the (Nothing-owned) CMF Buds Pro 2 last 26 hours. The CMF Buds 2 Plus offer 33 hours, while the EarFun Air Pro 4+ can go for 36 hours.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-privacy-concerns"><span>Nothing Ear (3a) review: Privacy concerns</span></h2><p>I wouldn’t normally need to write a section on privacy — and the fact that I have should raise alarm bells. The Ear (3a) have a range of recording features, which look fairly innocent on the surface. There’s an Audio Snapshot mode, which will store the last minute of media playback — i.e. your favorite excerpts from music or 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.25%;"><img id="AKaoFXC3XZh5NsWvnrdu4A" name="Nothing Ear (3a)_Screenshots-7" alt="A screenshot showing the Nothing Ear (3a) earbuds being customized via the Nothing X app" src="https://cdn.mos.cms.futurecdn.net/AKaoFXC3XZh5NsWvnrdu4A.jpg" mos="" align="middle" fullscreen="1" width="2000" height="1125" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/AKaoFXC3XZh5NsWvnrdu4A.jpg' target='_blank' class='expand-button icon-expand-image icon' ></a></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nothing / Future)</span></figcaption></figure><p>They can also record and store two hours of calls or voice notes, with a transcription service available via a subscription in the Nothing X app. Very handy for any students or journalists needing to record and transcribe online meetings. But I have serious privacy concerns with this feature. </p><p>During test calls, initiating ‘call recording’ delivered a prompt to both parties saying “this call will be recorded in 3, 2, 1.” Fine — good, even! Not so fast. After hitting mute on my side, the other party heard no prompt whatsoever. I ran the test with my colleague Nikita, then again with my wife. Same result for both. So, the Ear (3a) can record calls without the other person knowing or consenting. Here in the U.K., that isn’t illegal, but in many U.S. states it is. Regardless, it’s immoral unless under specific public interest needs.</p><p>I don’t like the idea of this feature becoming widespread in earbuds, either. Sure, there are already plenty of ways to record calls nefariously. But adding this kind of thing to tech as ubiquitous as earbuds feels like it’ll make non-consensual recording much easier and more commonplace.</p><p>I’ve reached out to Nothing for comment on this issue, but it hasn't got back to me yet.</p><h2 class="article-body__section" id="section-nothing-ear-3a-review-verdict"><span>Nothing Ear (3a) 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="hvhmuMmVooSD2tf9zDBhuc" name="Nothing Ear (3a)_Buds-4.JPG" alt="The Nothing Earbuds (3a) in pink, on a wooden surface" src="https://cdn.mos.cms.futurecdn.net/hvhmuMmVooSD2tf9zDBhuc.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Nothing Ear (3a) surprised me. I thought <em>nothing</em> could tempt me away from my long-term daily drivers, the EarFun Air Pro 4. But it seems <em>Nothing</em> can!</p><p>I really, really wish they didn’t make it so easy to spy on people, but, putting aside the privacy stuff just for a minute and judging these on their merits as earbuds, the Ear (3a) offer impressive performance for their price. Consider the striking design, premium build quality and decent comfort, and these buds start looking like a proper bargain — in fact, I’m not sure why you’d buy the pricier Nothing 3 flagship. </p><p>OK, the sound needs some work by default, but the drivers are inherently very good, and it only takes a little time in the EQ to sort everything out. For under $100, that’s time I’m more than happy to spend.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ National Vinyl Record Day: The 12 essential albums in my collection I listen to all the time ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Today is National Vinyl Record Player Day so, if you have a turntable it's time to dig out a few of your favorite records and give them a spin!</p><p>But perhaps you're looking for a few tips on how to fill out your shelves or even a place to start your collection? Music is so subjective and it's impossible to create a truly definitive list. For that reason I've broken my suggestions down into four sections. </p><p>First off I've picked out three must-buy all-timers, from legendary artists – think legends like David Bowie and Miles Davis. Then I've thrown in a few underrated classics (the Broadcast album is one of my favorite records ever) followed by some modern masterpieces, and rounding out with three of the year's best releases so far. All 12 of these films are well-worth your time and finding a copy on vinyl.</p><h2 id="all-timers">All-timers</h2>        <div class="featured_product_block featured_block_hero" data-id="9ae16730-965d-11f1-adeb-6963713ae772">            <a href="https://www.amazon.com/Hunky-Dory-180-Gram-Vinyl/dp/B01AJZ8ELC/" data-model-name="David Bowie – Hunky Dory" 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/6D3JyW25NdBrGbA33Zibz6.jpg" alt="Hunky Dory"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Rhino</div>                                        <div class="featured__title">David Bowie – Hunky Dory</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae166b8-965d-11f1-b99c-413c65e98ed4">            <a href="https://www.amazon.com/Pet-Sounds-Stereo-Beach-Boys/dp/B01DB0EBMS/" data-model-name="The Beach Boys – Pet Sounds" 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/GtHuqp9j2r6x2d8FQVxPjE.jpg" alt="The Beach Boys – Pet Sounds"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Capitol Music Group</div>                                        <div class="featured__title">The Beach Boys – Pet Sounds</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae1679e-965d-11f1-bd71-792bb943bdcd">            <a href="https://www.amazon.com/Kind-Blue-Vinyl-Miles-Davis/dp/B0041TM5OU/" data-model-name="Miles Davis – Kind of Blue" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/VsYFMKXuMNqHPdZMQbNXC8.jpg" alt="Kind of Blue (vinyl)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>LEGACY RECORDINGS</div>                                        <div class="featured__title">Miles Davis – Kind of Blue</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h2 id="underrated-classics">Underrated classics</h2>        <div class="featured_product_block featured_block_hero" data-id="9ae168e8-965d-11f1-a439-77e881e05c1d">            <a href="https://www.amazon.com/Spirit-Eden-Talk/dp/B0FLFGJYK8/" data-model-name="Talk Talk – Spirit of Eden" 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/qvZWphgoLc8jPEUSggNRTF.jpg" alt="Spirit of Eden"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Parlophone</div>                                        <div class="featured__title">Talk Talk – Spirit of Eden</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae1687a-965d-11f1-a107-8f5f4f2e5801">            <a href="https://www.amazon.com/RAM-LP-Paul-Linda-McCartney/dp/B0762CQ3CX/" data-model-name="Paul McCartney – Ram" 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/mrmNvUf9nXrv3EBmgBbPVA.jpg" alt="Ram [lp]"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Capitol Music Group</div>                                        <div class="featured__title">Paul McCartney – Ram</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="3c976f9a-9666-11f1-965c-75390efc5ec3">            <a href="https://www.amazon.com/Noise-Made-People-Vinyl/dp/B00SSL4EVE/" data-model-name="Broadcast – The Noise Made by People" 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/WupixRvgMhy8DraVqmsAtB.jpg" alt="The Noise Made by People"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warp Records</div>                                        <div class="featured__title">Broadcast – The Noise Made by People</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h2 id="modern-classics">Modern classics</h2>        <div class="featured_product_block featured_block_hero" data-id="9ae16aaa-965d-11f1-a05b-47fa8c400977">            <a href="https://www.amazon.com/Be-Cowboy-Mitski/dp/B07D3DBCCV/" data-model-name="Mitski – Be the Cowboy" 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/rk2nyv4fL97m8X9UNPVRtF.jpg" alt="Be the Cowboy"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Dead Oceans</div>                                        <div class="featured__title">Mitski – Be the Cowboy</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae169ce-965d-11f1-8218-8fb393f189f2">            <a href="https://www.amazon.com/Rainbows-Vinyl-Radiohead/dp/B000YIXBV8/" data-model-name="Radiohead – In Rainbows" 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/bdJGLw9BvN6THRPbkGbUqE.jpg" alt="In Rainbows"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>XL Recordings</div>                                        <div class="featured__title">Radiohead – In Rainbows</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae16a3c-965d-11f1-b495-c51b153769f8">            <a href="https://www.amazon.com/Good-Kid-m-d-City/dp/B009F1ZYO2/" data-model-name="Kendrick Lamar – Good Kid, M.a.a.d City: a Short Film" 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/kxe6Wogxdf5n5ViLv3VfcF.jpg" alt="Good Kid, M.a.a.d City: a Short Film"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>AfterMath</div>                                        <div class="featured__title">Kendrick Lamar – Good Kid, M.a.a.d City: a Short Film</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h2 id="the-best-of-2026-so-far">The best of 2026 (so far)</h2>        <div class="featured_product_block featured_block_hero" data-id="9ae16b86-965d-11f1-9083-3b569cbc30e1">            <a href="https://www.amazon.com/Vol-II-Angine-Poitrine/dp/B0GQNY9SJ7/" data-model-name="Angine De Poitrine – Vol. Ii" 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/sS3CwqVBShcxC9NeWVj3wS.jpg" alt="Angine De Poitrine - Vol 2 cover."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ato Records</div>                                        <div class="featured__title">Angine De Poitrine – Vol. Ii</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae16bf4-965d-11f1-ac61-090227b2e7db">            <a href="https://www.amazon.com/seem-pretty-spark-black-vinyl/dp/B0GVQ4ZWSF/" data-model-name="Olivia Rodrigo – You Seem Pretty Sad For A Girl So In Love" 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/pa38T6FxnLfmpte7XbscHA.jpg" alt="Olivia Rodrigo – You Seem Pretty Sad For A Girl So In Love"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>UNIVERSAL MUSIC GROUP</div>                                        <div class="featured__title">Olivia Rodrigo – You Seem Pretty Sad For A Girl So In Love</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="a8e4aa22-9667-11f1-a924-7124a3bc2710">            <a href="https://www.amazon.com/WOR-T-GIRL-AMERICA-Slayyyter/dp/B0GGLGLYR2/" data-model-name="Slayyyter – Wor$t Girl in America" 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/4DFppVWeqaTuFmmEs4tLyW.jpg" alt="Wor$t Girl in America"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>RCA</div>                                        <div class="featured__title">Slayyyter – Wor$t Girl in America</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/national-vinyl-record-day-the-12-essential-albums-in-my-collection-i-listen-to-all-the-time</link>
                                                                            <description>
                            <![CDATA[ Celebrate National Vinyl Record Day with our guide to 12 must-buys albums. From all-time classics to some of 2026's best releases, these are some of the records I'll be spinning today. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">mXuokDZ5hacEfyZdRjruZV</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/rYf2wkjigccuQmg76sobmS-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 12 Aug 2026 17:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Will Salmon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/HpRWP7Mh9ErgRGoTvZBbBJ.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Will Salmon is Tom&#039;s Guide&#039;s Collections Editor, curating advice on the best products to spend your money on. Will has been writing about tech and entertainment for almost 20 years, including stints as GamesRadar+&#039;s Streaming Editor, editing comics website Newsarama, and writing for lots and lots of magazines.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/rYf2wkjigccuQmg76sobmS-1280-80.jpg">
                                                            <media:credit><![CDATA[Parlophone, XL, Dead Oceans / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of vinyl records and a turntable]]></media:description>                                                            <media:text><![CDATA[A selection of vinyl records and a turntable]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of vinyl records and a turntable]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/rYf2wkjigccuQmg76sobmS-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Today is National Vinyl Record Player Day so, if you have a turntable it's time to dig out a few of your favorite records and give them a spin!</p><p>But perhaps you're looking for a few tips on how to fill out your shelves or even a place to start your collection? Music is so subjective and it's impossible to create a truly definitive list. For that reason I've broken my suggestions down into four sections. </p><p>First off I've picked out three must-buy all-timers, from legendary artists – think legends like David Bowie and Miles Davis. Then I've thrown in a few underrated classics (the Broadcast album is one of my favorite records ever) followed by some modern masterpieces, and rounding out with three of the year's best releases so far. All 12 of these films are well-worth your time and finding a copy on vinyl.</p><h2 id="all-timers">All-timers</h2>        <div class="featured_product_block featured_block_hero" data-id="9ae16730-965d-11f1-adeb-6963713ae772">            <a href="https://www.amazon.com/Hunky-Dory-180-Gram-Vinyl/dp/B01AJZ8ELC/" data-model-name="David Bowie – Hunky Dory" 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/6D3JyW25NdBrGbA33Zibz6.jpg" alt="Hunky Dory"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Rhino</div>                                        <div class="featured__title">David Bowie – Hunky Dory</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae166b8-965d-11f1-b99c-413c65e98ed4">            <a href="https://www.amazon.com/Pet-Sounds-Stereo-Beach-Boys/dp/B01DB0EBMS/" data-model-name="The Beach Boys – Pet Sounds" 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/GtHuqp9j2r6x2d8FQVxPjE.jpg" alt="The Beach Boys – Pet Sounds"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Capitol Music Group</div>                                        <div class="featured__title">The Beach Boys – Pet Sounds</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae1679e-965d-11f1-bd71-792bb943bdcd">            <a href="https://www.amazon.com/Kind-Blue-Vinyl-Miles-Davis/dp/B0041TM5OU/" data-model-name="Miles Davis – Kind of Blue" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/VsYFMKXuMNqHPdZMQbNXC8.jpg" alt="Kind of Blue (vinyl)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>LEGACY RECORDINGS</div>                                        <div class="featured__title">Miles Davis – Kind of Blue</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h2 id="underrated-classics">Underrated classics</h2>        <div class="featured_product_block featured_block_hero" data-id="9ae168e8-965d-11f1-a439-77e881e05c1d">            <a href="https://www.amazon.com/Spirit-Eden-Talk/dp/B0FLFGJYK8/" data-model-name="Talk Talk – Spirit of Eden" 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/qvZWphgoLc8jPEUSggNRTF.jpg" alt="Spirit of Eden"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Parlophone</div>                                        <div class="featured__title">Talk Talk – Spirit of Eden</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae1687a-965d-11f1-a107-8f5f4f2e5801">            <a href="https://www.amazon.com/RAM-LP-Paul-Linda-McCartney/dp/B0762CQ3CX/" data-model-name="Paul McCartney – Ram" 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/mrmNvUf9nXrv3EBmgBbPVA.jpg" alt="Ram [lp]"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Capitol Music Group</div>                                        <div class="featured__title">Paul McCartney – Ram</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="3c976f9a-9666-11f1-965c-75390efc5ec3">            <a href="https://www.amazon.com/Noise-Made-People-Vinyl/dp/B00SSL4EVE/" data-model-name="Broadcast – The Noise Made by People" 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/WupixRvgMhy8DraVqmsAtB.jpg" alt="The Noise Made by People"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Warp Records</div>                                        <div class="featured__title">Broadcast – The Noise Made by People</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h2 id="modern-classics">Modern classics</h2>        <div class="featured_product_block featured_block_hero" data-id="9ae16aaa-965d-11f1-a05b-47fa8c400977">            <a href="https://www.amazon.com/Be-Cowboy-Mitski/dp/B07D3DBCCV/" data-model-name="Mitski – Be the Cowboy" 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/rk2nyv4fL97m8X9UNPVRtF.jpg" alt="Be the Cowboy"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Dead Oceans</div>                                        <div class="featured__title">Mitski – Be the Cowboy</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae169ce-965d-11f1-8218-8fb393f189f2">            <a href="https://www.amazon.com/Rainbows-Vinyl-Radiohead/dp/B000YIXBV8/" data-model-name="Radiohead – In Rainbows" 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/bdJGLw9BvN6THRPbkGbUqE.jpg" alt="In Rainbows"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>XL Recordings</div>                                        <div class="featured__title">Radiohead – In Rainbows</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae16a3c-965d-11f1-b495-c51b153769f8">            <a href="https://www.amazon.com/Good-Kid-m-d-City/dp/B009F1ZYO2/" data-model-name="Kendrick Lamar – Good Kid, M.a.a.d City: a Short Film" 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/kxe6Wogxdf5n5ViLv3VfcF.jpg" alt="Good Kid, M.a.a.d City: a Short Film"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>AfterMath</div>                                        <div class="featured__title">Kendrick Lamar – Good Kid, M.a.a.d City: a Short Film</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h2 id="the-best-of-2026-so-far">The best of 2026 (so far)</h2>        <div class="featured_product_block featured_block_hero" data-id="9ae16b86-965d-11f1-9083-3b569cbc30e1">            <a href="https://www.amazon.com/Vol-II-Angine-Poitrine/dp/B0GQNY9SJ7/" data-model-name="Angine De Poitrine – Vol. Ii" 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/sS3CwqVBShcxC9NeWVj3wS.jpg" alt="Angine De Poitrine - Vol 2 cover."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Ato Records</div>                                        <div class="featured__title">Angine De Poitrine – Vol. Ii</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9ae16bf4-965d-11f1-ac61-090227b2e7db">            <a href="https://www.amazon.com/seem-pretty-spark-black-vinyl/dp/B0GVQ4ZWSF/" data-model-name="Olivia Rodrigo – You Seem Pretty Sad For A Girl So In Love" 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/pa38T6FxnLfmpte7XbscHA.jpg" alt="Olivia Rodrigo – You Seem Pretty Sad For A Girl So In Love"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>UNIVERSAL MUSIC GROUP</div>                                        <div class="featured__title">Olivia Rodrigo – You Seem Pretty Sad For A Girl So In Love</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="a8e4aa22-9667-11f1-a924-7124a3bc2710">            <a href="https://www.amazon.com/WOR-T-GIRL-AMERICA-Slayyyter/dp/B0GGLGLYR2/" data-model-name="Slayyyter – Wor$t Girl in America" 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/4DFppVWeqaTuFmmEs4tLyW.jpg" alt="Wor$t Girl in America"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>RCA</div>                                        <div class="featured__title">Slayyyter – Wor$t Girl in America</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ ‘It’s more of a lifestyle, a fashionable device, not just tech’: We Are Rewind’s CEO on how the Pink Floyd collab is turning cassette players into ‘collector’s items’ ]]></title>
                                                                                                <dc:content><![CDATA[ <p>A lot of my childhood memories revolve around family roadtrips, and on one such roadtrip, I vividly remember my dad inserting a CD with a white triangular prism on a black background, the colors of the rainbow emerging from one side, into his car's player. I listened to the entirety of that 10-track album and though I didn't understand many of the lyrics as a 7-year-old, the album stuck with me.</p><p>I didn't even have to name the album and you're already thinking of it. Pink Floyd's "The Dark Side of the Moon" is nothing short of iconic. It's my favorite album of all time (I've even got a tattoo dedicated to it), so when the We Are Rewind x Pink Floyd WE-001 cassette player landed on my desk, I instantly fell in love.</p><p>And We Are Rewind's CEO, Romain Boudruche, echoes my sentiments. Speaking with him about the inspiration behind the collab, the popularity of cassettes (alongside vinyl) among Gen Z, and how cassettes are becoming "collector's items" will give anyone newfound appreciation for physical media.</p><div class="product"><a data-dimension112="2648ceae-95c7-11f1-8718-396baab2ec77" data-action="Deal Block" data-label="Cassette Player [PINK FLOYD] Limited Edition" data-dimension48="Cassette Player [PINK FLOYD] Limited Edition" data-dimension25="$199" href="https://www.wearerewind.com/products/lecteur-cassette-pink-floyd-edition-limitee" 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="fdeRR5oRBZAdtKYcPUvt68" name="we_are_rewind_we_001_pf_portable_bt_cassette_player_1785862929_1991359" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/fdeRR5oRBZAdtKYcPUvt68.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.wearerewind.com/products/lecteur-cassette-pink-floyd-edition-limitee" target="_blank" rel="nofollow" data-dimension112="2648ceae-95c7-11f1-8718-396baab2ec77" data-action="Deal Block" data-label="Cassette Player [PINK FLOYD] Limited Edition" data-dimension48="Cassette Player [PINK FLOYD] Limited Edition" data-dimension25="$199">Cassette Player [PINK FLOYD] Limited Edition: $199</a></strong><br>The Pink Floyd edition cassette player is currently out of stock, but the company hopes to have more in November. However, its non-branded cassette players are available for $159.<a class="view-deal button" href="https://www.wearerewind.com/products/lecteur-cassette-pink-floyd-edition-limitee" target="_blank" rel="nofollow" data-dimension112="2648ceae-95c7-11f1-8718-396baab2ec77" data-action="Deal Block" data-label="Cassette Player [PINK FLOYD] Limited Edition" data-dimension48="Cassette Player [PINK FLOYD] Limited Edition" data-dimension25="$199">View Deal</a></p></div><h2 id="today-you-don-t-own-what-you-pay-for">'Today, you don't own what you pay 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:2938px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="54SYs5rwa9773nEzrKD7gf" name="We_Are_Rewind_Pink_Floyd-lede" alt="A woman holding a We Are Rewind x Pink Floyd cassette player and a The Dark Side of the Moon cassette" src="https://cdn.mos.cms.futurecdn.net/54SYs5rwa9773nEzrKD7gf.jpg" mos="" align="middle" fullscreen="" width="2938" height="1653" 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>Physical media has seen a comeback in recent years, with vinyl purchases in 2025 surpassing $1.04 billion in the U.S. alone, <a href="https://www.riaa.com/wp-content/uploads/2026/03/RIAA-Year-End-Revenue-2025.pdf" target="_blank" rel="nofollow">according to a report by the Recording Industry Association of America</a>. On the flipside, <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 announced that it would be ending physical disc production</a> for PlayStation consoles in 2028. Why can't we own what we pay for now?</p><p>We Are Rewind is doing what it can to keep physical media alive. The French company was founded by two friends who wanted to bring back the cassette player — and that resonates with Gen Z. "Gen Z really want to hold things in their hands," Romain tells me. "You pay for your Netflix and Spotify subscriptions but you never hold anything in your hands. You don't own what you pay for. It's both a global saturation of digital things and that, for example, when you're listening to music, you're constantly distracted by notifications."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2757px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="a9vu4H9z3qLc5JovTGp5cP" name="We_Are_Rewind_Pink_Floyd-6" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/a9vu4H9z3qLc5JovTGp5cP.jpg" mos="" align="middle" fullscreen="" width="2757" height="1551" 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>That's something I believe all of us can relate to. </p><p>"I listen to music on my phone and then I have a pop-up from Instagram, and I click on it, and then I realize I've been scrolling for hours, for nothing,"  Romain says. </p><p>And that's where <a href="https://www.tomsguide.com/audio/we-are-rewind-cassette-player-review">We Are Rewind's WE-001</a> cassette player comes into play. "Holding a cassette player is a way to be very focused on what you're doing in that moment, and you're committed to it."</p><div><blockquote><p>Holding a cassette player is a way to be very focused on what you're doing in that moment, and you're committed to it.</p><p>Romain Boudruche, We Are Rewind CEO</p></blockquote></div><p>Alongside eliminating doomscrolling, cassette players invoke a feeling of nostalgia. I, personally, grew up in a time when I was burning my own CDs. The generation before mine was doing the same with mixtapes. </p><p>"I think there's something reassuring about projecting yourself in past times, even for people who didn't grow up in the 1970s or  80s," Romain says. "They are nostalgic for a period of time they didn't know but it's a way to go back, because they think of it as a less stressful time.</p><p>"Back in the day, you didn't worry about, 'What's the future going to be like?' I know there were a lot of scary things in the 80s too, but for those who didn't grow up in that period, it feels safe."</p><p>And Romain isn't wrong. In the age of social media where there's an overload of information, disinformation and fearmongering, it's hard not to worry about the future. Music, for me, has always been a safe space, and having a screen-free cassette player helps keep it intact.</p><h2 id="it-s-more-of-an-art-object">'It's more of an art object'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4220px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="FppAcumrCBv8RqjzizWD2R" name="We_Are_Rewind_Pink_Floyd-7" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/FppAcumrCBv8RqjzizWD2R.jpg" mos="" align="middle" fullscreen="" width="4220" height="2374" 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 We Are Rewind WE-001 was first released in 2023 and since then, Romain has been working closely with his team to release the limited edition Pink Floyd collab, to pay homage to one of his favorite bands. </p><p>And the Pink Floyd version is a thing of beauty. I've been using it for a couple of weeks and it's sublime: from its aluminum body and tactile buttons, to its modern features like USB-C charging and Bluetooth 5.1 support, it's a nifty gadget.</p><p>For Romain, it's more than that. "It's more of a lifestyle, a fashionable device," he tells me with a big smile on his face, "and it's more of an art object, not just tech." </p><p>The WE-001 being made of aluminum plays a big role in it feeling like a "collector's item" and not just a toy, so how did the cassette player come about?</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4441px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dAtxmVi6jDRAcweCYcnjsQ" name="We_Are_Rewind_Pink_Floyd-5" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/dAtxmVi6jDRAcweCYcnjsQ.jpg" mos="" align="middle" fullscreen="" width="4441" height="2498" 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><div><blockquote><p>You know when the kid picks up the glasses and the guy says, 'Are they heavy? Then they're expensive.' Every time we can, we like to use premium aluminum materials in our products.</p><p>Romain Boudruche, We Are Rewind CEO</p></blockquote></div><p>"It started with an article I read which said that cassettes were making a comeback thanks to 'The Guardians of the Galaxy,' " Romain laughs. "I saw that there are only plastic players, poor quality ones, in the market. We wanted to do something very close to the first Sony Walkman — one I held in my hands, and it was cold and heavy, and I wanted that, and we did it. The WE-001 gives the impression of something strong and something that's going to last long."</p><p>You're getting bang for your buck with the WE-001. The Pink Floyd version is priced at <a href="https://www.wearerewind.com/products/lecteur-cassette-pink-floyd-edition-limitee" target="_blank" rel="nofollow">$199 / £159</a>, and when explaining the price to me, Romain cites a quote from the <a href="https://www.youtube.com/watch?v=OA1vmN-skXo" target="_blank" rel="nofollow">1993 blockbuster, "Jurassic Park</a>." </p><p>"You know when the kid picks up the glasses and the guy says, 'Are they heavy? Then they're expensive.' Every time we can, we like to use premium aluminum materials in our products, like in the <a href="https://www.wearerewind.com/products/casque-audio-freddie" target="_blank" rel="nofollow">Freddie Headphones</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:4237px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="MtQsWpLPJzFJDeYXawopHR" name="We_Are_Rewind_Pink_Floyd" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/MtQsWpLPJzFJDeYXawopHR.jpg" mos="" align="middle" fullscreen="" width="4237" height="2383" 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 Pink Floyd WE-001 is certainly a collector's item, and each box includes an official 50th anniversary remastered edition of "The Dark Side of the Moon" (exclusive, because there's nowhere else to get it). The first batch consisted of just 3,000 units — and they sold out within a week. </p><p>I was lucky number 834, as mentioned on a little card inside the cassette player box. We Are Rewind is currently working on a second batch, expected to be available around November. "Just in time for Christmas!" exclaims Romain.</p><h2 id="a-50-50-split-between-the-older-and-younger-audiences">'A 50/50 split between the older and younger audiences'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4768px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vA8ebTuBpJ7xUyzLUE5wWR" name="We_Are_Rewind_Pink_Floyd-3" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/vA8ebTuBpJ7xUyzLUE5wWR.jpg" mos="" align="middle" fullscreen="" width="4768" height="2682" 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 We Are Rewind x Pink Floyd collaboration feels special. Creating a product that pays homage to one of the greatest rock bands of all time is no mean feat but We Are Rewind has done it, and there's a lot more Romain wants to do. When asked if there are any bucket list artists he wants to work with, or design products inspired by them, Romain was quick to answer.</p><p>"The 'White Album' by The Beatles, definitely! We would like to make an entire white boxset dedicated to it, hopefully for the anniversary in 2028. There's things we want to do with Gorillaz and Daft Punk, too, and David Bowie. But I don't think we can go higher than Pink Floyd and Dark Side. It's the best album of all time, and it looks fantastic. As soon as I opened the box, I was like, 'Okay, I'm in love.'"</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4786px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="MgDh9qu9kEybkRzhHgFhWR" name="We_Are_Rewind_Pink_Floyd-8" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/MgDh9qu9kEybkRzhHgFhWR.jpg" mos="" align="middle" fullscreen="" width="4786" height="2692" 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>He is, of course, not the only one. Given the limited edition's popularity and how quickly it sold out, I had to ask Romain about who was buying it. Gen Z? Gen X? Millennials? While he didn't have the data to hand, Romain believes that it's "a 50/50 split between the older and younger audiences."</p><p>"It's something that we didn't expect in the first place as we were going to target only people who were familiar with cassette players, so older generations," he adds, "and it's totally different now. We have people between 50 and 70 as well as very young people between 17 and 25 buying the player." Maybe you were one of them, dear reader, or you could be when the next batch drops.</p><p>While cassette sales are up by <a href="https://www.forbes.com/sites/billrosenblatt/2026/02/04/2025-music-data-taylor-swift-is-album-queen-as-charts-become-fickle/" target="_blank" rel="nofollow">17.5% in the U.S.</a>, and <a href="https://www.officialcharts.com/chart-news/best-selling-cassettes-singles-albums-2025/" target="_blank" rel="nofollow">up by 53% from 2024 to 2025 in the U.K.</a>, Romain doesn't think they will become as popular as vinyl. "I wish it would but I don't think it will," he says. </p><p>"Physical and running issues with cassettes can be problematic and something people struggle with. It's definitely going to keep growing in popularity in the coming years, I'm convinced about that, but I don't think it will reach the peaks vinyl has."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4383px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="FnJfqmM8c99ER4DjWoyyAR" name="We_Are_Rewind_Pink_Floyd-2" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/FnJfqmM8c99ER4DjWoyyAR.jpg" mos="" align="middle" fullscreen="" width="4383" height="2465" 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>Even if they don't become as popular as vinyl has, I don't believe that's a problem. As long as they're still around and available to buy, and a company like We Are Rewind is actively working towards making the format more accessible, perhaps that's all we need right now.</p><p>For Romain, even having just the option to buy cassettes is a boon. "When you are in love with an artist, you want to hold something from them, which is why I collect CDs, cassettes and records, and sometimes, it's nice to just display them and show your love for them."</p><p>And in an age of digital saturation where even one screen can feel overwhelming, We Are Rewind has nailed its WE-001 cassette player, striking the perfect balance between old and new. The Dark Side of the Moon edition is perhaps one of the the most special gadgets — or rather, pieces of art — I've owned.</p><p>I'm going to Breathe (In the Air), take my Time, maybe go On the Run, and enjoy this sonic masterpiece like it's The Great Gig in the Sky. On tape. As Pink Floyd intended.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OdRK5e"></div>                            </div>                            <script src="https://kwizly.com/embed/OdRK5e.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/ive-been-testing-this-cassette-walkman-and-im-having-a-blast-reliving-my-childhood">I've been testing this cassette walkman, and I'm having a blast reliving my childhood</a></li><li><a href="https://www.tomsguide.com/audio/my-coworker-has-a-double-life-inside-the-underground-tape-label-defying-algorithm-culture">Inside the underground cassette tape label reviving physical media (and defying algorithm culture)</a></li><li><a href="https://www.tomsguide.com/audio/digital-fatigue-is-showing-up-in-our-music-consumption-and-gen-z-is-ditching-streaming-for-vinyl-crackle">Digital fatigue is showing up in our music consumption — and Gen Z is ditching streaming for vinyl crackle</a></li></ul><div data-widget-type="review" data-model-name="We Are Rewind Portable Cassette Player with Bluetooth"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/we-are-rewind-pink-floyd-cassette-player-collab-ceo-interview</link>
                                                                            <description>
                            <![CDATA[ I spoke with We Are Rewind CEO Romain Boudruche about the inspiration behind the Pink Floyd WE-001, cassettes coming back, and his plans for future collabs. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">oGnhnwzaCmBYe7XfLwoCJj</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/Ru438oeMsbvpriJBPCwMrP-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 12 Aug 2026 09:00:00 +0000</pubDate>                                                                                                                                                                                                                                <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.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/Ru438oeMsbvpriJBPCwMrP-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[We Are Rewind x Pink Floyd WE-001 cassette player]]></media:description>                                                            <media:text><![CDATA[We Are Rewind x Pink Floyd WE-001 cassette player]]></media:text>
                                <media:title type="plain"><![CDATA[We Are Rewind x Pink Floyd WE-001 cassette player]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/Ru438oeMsbvpriJBPCwMrP-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>A lot of my childhood memories revolve around family roadtrips, and on one such roadtrip, I vividly remember my dad inserting a CD with a white triangular prism on a black background, the colors of the rainbow emerging from one side, into his car's player. I listened to the entirety of that 10-track album and though I didn't understand many of the lyrics as a 7-year-old, the album stuck with me.</p><p>I didn't even have to name the album and you're already thinking of it. Pink Floyd's "The Dark Side of the Moon" is nothing short of iconic. It's my favorite album of all time (I've even got a tattoo dedicated to it), so when the We Are Rewind x Pink Floyd WE-001 cassette player landed on my desk, I instantly fell in love.</p><p>And We Are Rewind's CEO, Romain Boudruche, echoes my sentiments. Speaking with him about the inspiration behind the collab, the popularity of cassettes (alongside vinyl) among Gen Z, and how cassettes are becoming "collector's items" will give anyone newfound appreciation for physical media.</p><div class="product"><a data-dimension112="2648ceae-95c7-11f1-8718-396baab2ec77" data-action="Deal Block" data-label="Cassette Player [PINK FLOYD] Limited Edition" data-dimension48="Cassette Player [PINK FLOYD] Limited Edition" data-dimension25="$199" href="https://www.wearerewind.com/products/lecteur-cassette-pink-floyd-edition-limitee" 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="fdeRR5oRBZAdtKYcPUvt68" name="we_are_rewind_we_001_pf_portable_bt_cassette_player_1785862929_1991359" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/fdeRR5oRBZAdtKYcPUvt68.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.wearerewind.com/products/lecteur-cassette-pink-floyd-edition-limitee" target="_blank" rel="nofollow" data-dimension112="2648ceae-95c7-11f1-8718-396baab2ec77" data-action="Deal Block" data-label="Cassette Player [PINK FLOYD] Limited Edition" data-dimension48="Cassette Player [PINK FLOYD] Limited Edition" data-dimension25="$199">Cassette Player [PINK FLOYD] Limited Edition: $199</a></strong><br>The Pink Floyd edition cassette player is currently out of stock, but the company hopes to have more in November. However, its non-branded cassette players are available for $159.<a class="view-deal button" href="https://www.wearerewind.com/products/lecteur-cassette-pink-floyd-edition-limitee" target="_blank" rel="nofollow" data-dimension112="2648ceae-95c7-11f1-8718-396baab2ec77" data-action="Deal Block" data-label="Cassette Player [PINK FLOYD] Limited Edition" data-dimension48="Cassette Player [PINK FLOYD] Limited Edition" data-dimension25="$199">View Deal</a></p></div><h2 id="today-you-don-t-own-what-you-pay-for">'Today, you don't own what you pay 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:2938px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="54SYs5rwa9773nEzrKD7gf" name="We_Are_Rewind_Pink_Floyd-lede" alt="A woman holding a We Are Rewind x Pink Floyd cassette player and a The Dark Side of the Moon cassette" src="https://cdn.mos.cms.futurecdn.net/54SYs5rwa9773nEzrKD7gf.jpg" mos="" align="middle" fullscreen="" width="2938" height="1653" 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>Physical media has seen a comeback in recent years, with vinyl purchases in 2025 surpassing $1.04 billion in the U.S. alone, <a href="https://www.riaa.com/wp-content/uploads/2026/03/RIAA-Year-End-Revenue-2025.pdf" target="_blank" rel="nofollow">according to a report by the Recording Industry Association of America</a>. On the flipside, <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 announced that it would be ending physical disc production</a> for PlayStation consoles in 2028. Why can't we own what we pay for now?</p><p>We Are Rewind is doing what it can to keep physical media alive. The French company was founded by two friends who wanted to bring back the cassette player — and that resonates with Gen Z. "Gen Z really want to hold things in their hands," Romain tells me. "You pay for your Netflix and Spotify subscriptions but you never hold anything in your hands. You don't own what you pay for. It's both a global saturation of digital things and that, for example, when you're listening to music, you're constantly distracted by notifications."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2757px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="a9vu4H9z3qLc5JovTGp5cP" name="We_Are_Rewind_Pink_Floyd-6" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/a9vu4H9z3qLc5JovTGp5cP.jpg" mos="" align="middle" fullscreen="" width="2757" height="1551" 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>That's something I believe all of us can relate to. </p><p>"I listen to music on my phone and then I have a pop-up from Instagram, and I click on it, and then I realize I've been scrolling for hours, for nothing,"  Romain says. </p><p>And that's where <a href="https://www.tomsguide.com/audio/we-are-rewind-cassette-player-review">We Are Rewind's WE-001</a> cassette player comes into play. "Holding a cassette player is a way to be very focused on what you're doing in that moment, and you're committed to it."</p><div><blockquote><p>Holding a cassette player is a way to be very focused on what you're doing in that moment, and you're committed to it.</p><p>Romain Boudruche, We Are Rewind CEO</p></blockquote></div><p>Alongside eliminating doomscrolling, cassette players invoke a feeling of nostalgia. I, personally, grew up in a time when I was burning my own CDs. The generation before mine was doing the same with mixtapes. </p><p>"I think there's something reassuring about projecting yourself in past times, even for people who didn't grow up in the 1970s or  80s," Romain says. "They are nostalgic for a period of time they didn't know but it's a way to go back, because they think of it as a less stressful time.</p><p>"Back in the day, you didn't worry about, 'What's the future going to be like?' I know there were a lot of scary things in the 80s too, but for those who didn't grow up in that period, it feels safe."</p><p>And Romain isn't wrong. In the age of social media where there's an overload of information, disinformation and fearmongering, it's hard not to worry about the future. Music, for me, has always been a safe space, and having a screen-free cassette player helps keep it intact.</p><h2 id="it-s-more-of-an-art-object">'It's more of an art object'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4220px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="FppAcumrCBv8RqjzizWD2R" name="We_Are_Rewind_Pink_Floyd-7" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/FppAcumrCBv8RqjzizWD2R.jpg" mos="" align="middle" fullscreen="" width="4220" height="2374" 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 We Are Rewind WE-001 was first released in 2023 and since then, Romain has been working closely with his team to release the limited edition Pink Floyd collab, to pay homage to one of his favorite bands. </p><p>And the Pink Floyd version is a thing of beauty. I've been using it for a couple of weeks and it's sublime: from its aluminum body and tactile buttons, to its modern features like USB-C charging and Bluetooth 5.1 support, it's a nifty gadget.</p><p>For Romain, it's more than that. "It's more of a lifestyle, a fashionable device," he tells me with a big smile on his face, "and it's more of an art object, not just tech." </p><p>The WE-001 being made of aluminum plays a big role in it feeling like a "collector's item" and not just a toy, so how did the cassette player come about?</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4441px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dAtxmVi6jDRAcweCYcnjsQ" name="We_Are_Rewind_Pink_Floyd-5" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/dAtxmVi6jDRAcweCYcnjsQ.jpg" mos="" align="middle" fullscreen="" width="4441" height="2498" 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><div><blockquote><p>You know when the kid picks up the glasses and the guy says, 'Are they heavy? Then they're expensive.' Every time we can, we like to use premium aluminum materials in our products.</p><p>Romain Boudruche, We Are Rewind CEO</p></blockquote></div><p>"It started with an article I read which said that cassettes were making a comeback thanks to 'The Guardians of the Galaxy,' " Romain laughs. "I saw that there are only plastic players, poor quality ones, in the market. We wanted to do something very close to the first Sony Walkman — one I held in my hands, and it was cold and heavy, and I wanted that, and we did it. The WE-001 gives the impression of something strong and something that's going to last long."</p><p>You're getting bang for your buck with the WE-001. The Pink Floyd version is priced at <a href="https://www.wearerewind.com/products/lecteur-cassette-pink-floyd-edition-limitee" target="_blank" rel="nofollow">$199 / £159</a>, and when explaining the price to me, Romain cites a quote from the <a href="https://www.youtube.com/watch?v=OA1vmN-skXo" target="_blank" rel="nofollow">1993 blockbuster, "Jurassic Park</a>." </p><p>"You know when the kid picks up the glasses and the guy says, 'Are they heavy? Then they're expensive.' Every time we can, we like to use premium aluminum materials in our products, like in the <a href="https://www.wearerewind.com/products/casque-audio-freddie" target="_blank" rel="nofollow">Freddie Headphones</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:4237px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="MtQsWpLPJzFJDeYXawopHR" name="We_Are_Rewind_Pink_Floyd" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/MtQsWpLPJzFJDeYXawopHR.jpg" mos="" align="middle" fullscreen="" width="4237" height="2383" 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 Pink Floyd WE-001 is certainly a collector's item, and each box includes an official 50th anniversary remastered edition of "The Dark Side of the Moon" (exclusive, because there's nowhere else to get it). The first batch consisted of just 3,000 units — and they sold out within a week. </p><p>I was lucky number 834, as mentioned on a little card inside the cassette player box. We Are Rewind is currently working on a second batch, expected to be available around November. "Just in time for Christmas!" exclaims Romain.</p><h2 id="a-50-50-split-between-the-older-and-younger-audiences">'A 50/50 split between the older and younger audiences'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4768px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vA8ebTuBpJ7xUyzLUE5wWR" name="We_Are_Rewind_Pink_Floyd-3" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/vA8ebTuBpJ7xUyzLUE5wWR.jpg" mos="" align="middle" fullscreen="" width="4768" height="2682" 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 We Are Rewind x Pink Floyd collaboration feels special. Creating a product that pays homage to one of the greatest rock bands of all time is no mean feat but We Are Rewind has done it, and there's a lot more Romain wants to do. When asked if there are any bucket list artists he wants to work with, or design products inspired by them, Romain was quick to answer.</p><p>"The 'White Album' by The Beatles, definitely! We would like to make an entire white boxset dedicated to it, hopefully for the anniversary in 2028. There's things we want to do with Gorillaz and Daft Punk, too, and David Bowie. But I don't think we can go higher than Pink Floyd and Dark Side. It's the best album of all time, and it looks fantastic. As soon as I opened the box, I was like, 'Okay, I'm in love.'"</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4786px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="MgDh9qu9kEybkRzhHgFhWR" name="We_Are_Rewind_Pink_Floyd-8" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/MgDh9qu9kEybkRzhHgFhWR.jpg" mos="" align="middle" fullscreen="" width="4786" height="2692" 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>He is, of course, not the only one. Given the limited edition's popularity and how quickly it sold out, I had to ask Romain about who was buying it. Gen Z? Gen X? Millennials? While he didn't have the data to hand, Romain believes that it's "a 50/50 split between the older and younger audiences."</p><p>"It's something that we didn't expect in the first place as we were going to target only people who were familiar with cassette players, so older generations," he adds, "and it's totally different now. We have people between 50 and 70 as well as very young people between 17 and 25 buying the player." Maybe you were one of them, dear reader, or you could be when the next batch drops.</p><p>While cassette sales are up by <a href="https://www.forbes.com/sites/billrosenblatt/2026/02/04/2025-music-data-taylor-swift-is-album-queen-as-charts-become-fickle/" target="_blank" rel="nofollow">17.5% in the U.S.</a>, and <a href="https://www.officialcharts.com/chart-news/best-selling-cassettes-singles-albums-2025/" target="_blank" rel="nofollow">up by 53% from 2024 to 2025 in the U.K.</a>, Romain doesn't think they will become as popular as vinyl. "I wish it would but I don't think it will," he says. </p><p>"Physical and running issues with cassettes can be problematic and something people struggle with. It's definitely going to keep growing in popularity in the coming years, I'm convinced about that, but I don't think it will reach the peaks vinyl has."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4383px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="FnJfqmM8c99ER4DjWoyyAR" name="We_Are_Rewind_Pink_Floyd-2" alt="We Are Rewind x Pink Floyd WE-001 cassette player" src="https://cdn.mos.cms.futurecdn.net/FnJfqmM8c99ER4DjWoyyAR.jpg" mos="" align="middle" fullscreen="" width="4383" height="2465" 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>Even if they don't become as popular as vinyl has, I don't believe that's a problem. As long as they're still around and available to buy, and a company like We Are Rewind is actively working towards making the format more accessible, perhaps that's all we need right now.</p><p>For Romain, even having just the option to buy cassettes is a boon. "When you are in love with an artist, you want to hold something from them, which is why I collect CDs, cassettes and records, and sometimes, it's nice to just display them and show your love for them."</p><p>And in an age of digital saturation where even one screen can feel overwhelming, We Are Rewind has nailed its WE-001 cassette player, striking the perfect balance between old and new. The Dark Side of the Moon edition is perhaps one of the the most special gadgets — or rather, pieces of art — I've owned.</p><p>I'm going to Breathe (In the Air), take my Time, maybe go On the Run, and enjoy this sonic masterpiece like it's The Great Gig in the Sky. On tape. As Pink Floyd intended.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OdRK5e"></div>                            </div>                            <script src="https://kwizly.com/embed/OdRK5e.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/ive-been-testing-this-cassette-walkman-and-im-having-a-blast-reliving-my-childhood">I've been testing this cassette walkman, and I'm having a blast reliving my childhood</a></li><li><a href="https://www.tomsguide.com/audio/my-coworker-has-a-double-life-inside-the-underground-tape-label-defying-algorithm-culture">Inside the underground cassette tape label reviving physical media (and defying algorithm culture)</a></li><li><a href="https://www.tomsguide.com/audio/digital-fatigue-is-showing-up-in-our-music-consumption-and-gen-z-is-ditching-streaming-for-vinyl-crackle">Digital fatigue is showing up in our music consumption — and Gen Z is ditching streaming for vinyl crackle</a></li></ul><div data-widget-type="review" data-model-name="We Are Rewind Portable Cassette Player with Bluetooth"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I found 9 Bose deals on Amazon that are actually worth your money — up to 36% off right now ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Bose makes some of the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best headphones</a> and <a href="https://www.tomsguide.com/best-picks/best-waterproof-speakers">best waterproof Bluetooth speakers</a> on the market. If you're in the market for some new gear from the popular audio brand you're in luck: Amazon is currently knocking up to 36% off Bose devices. </p><p>Looking for a new speaker? I recommend <a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow">the Bose SoundLink Flex (Gen 2) for just $119</a>. When it comes to headphones, you can't go wrong with the <a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0CCZ26B5V" target="_blank" rel="nofollow">Bose QuietComfort Headphones for $229</a>, which are currently $130 off. </p><p>We don't know how long these rare Bose deals will last, so we recommending adding to cart ASAP. Keep scrolling to check out the rest of my favorite picks from the sale. </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links </span></h3><ul><li><a href="https://www.amazon.com/stores/Bose/page/9E24AFD5-B420-4859-BD2F-A993BA579B20?" target="_blank" rel="nofollow"><strong>shop all Bose deals on Amazon</strong></a></li><li><strong>Bose SoundLink Micro: </strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FF4RY23S/" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Bose SoundLink Flex (Gen 2): </strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow"><strong>was $149 now $119</strong></a></li><li><strong>Bose QuietComfort Bluetooth Earbud: </strong><a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Lifestyle-Cancellation/dp/B0D8BWD76H" target="_blank" rel="nofollow"><strong>was $179 now $149</strong></a></li><li><strong>Bose Ultra Open Earbuds: </strong><a href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><strong>was $299 now $199</strong></a></li><li><strong>Bose QuietComfort Headphones: </strong><a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0CCZ26B5V" target="_blank" rel="nofollow"><strong>was $349 now $229</strong></a></li><li><strong>Bose SoundLink Max: </strong><a href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow"><strong>was $399 now $309</strong></a></li></ul><h3 class="article-body__section" id="section-best-bose-deals"><span>Best Bose Deals</span></h3><div class="product"><a data-dimension112="a2f9442a-94c7-11f1-a785-91173c6eac9d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FF4RY23S/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1105px;"><p class="vanilla-image-block" style="padding-top:109.50%;"><img id="jBQUxpMSaZYZyyD2LJmGD7" name="Bose Micro" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jBQUxpMSaZYZyyD2LJmGD7.jpg" mos="" align="middle" fullscreen="" width="1105" height="1210" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FF4RY23S/" target="_blank" rel="nofollow" data-dimension112="a2f9442a-94c7-11f1-a785-91173c6eac9d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99">SoundLink Micro Portable Bluetooth Speaker: was $129 now $99</a></strong><br>The SoundLink Micro is a compact, portable speaker you can take on the road. The Bluetooth speaker is waterproof (IPX7 certified) and offers up to 6 hours of play time off a single charge. It can also be paired with another SoundLink speaker to create a stereo mode.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FF4RY23S/" target="_blank" rel="nofollow" data-dimension112="a2f9442a-94c7-11f1-a785-91173c6eac9d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94498-94c7-11f1-81aa-a386e4e4cdd5" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:75.11%;"><img id="fexRzMYRvYZTHGfbcUbFf8" name="Bose speaker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/fexRzMYRvYZTHGfbcUbFf8.jpg" mos="" align="middle" fullscreen="" width="679" height="510" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow" data-dimension112="a2f94498-94c7-11f1-81aa-a386e4e4cdd5" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119">SoundLink Flex (Gen 2): was $149 now $119</a></strong><br>The new Bose SoundLink (2nd gen) is a compact and powerful Bluetooth speaker with a durable design. This 1.2-pound speaker is water and dust proof and delivered great sound quality in our <a href="https://www.tomsguide.com/audio/bose-soundlink-flex-2-review" target="_blank">SoundLink Flex gen 2 review</a>. With 12 hours of battery life and $30 off, you have yourself a great deal.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow" data-dimension112="a2f94498-94c7-11f1-81aa-a386e4e4cdd5" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94506-94c7-11f1-96bb-69dd07576d6d" data-action="Deal Block" data-label="QuietComfort Bluetooth Earbud" data-dimension48="QuietComfort Bluetooth Earbud" data-dimension25="$149" href="https://www.amazon.com/dp/B0FLYJ56H9?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:95.43%;"><img id="tEtgPEcP9tofgugi8QdrcP" name="Bose quietcomfort" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/tEtgPEcP9tofgugi8QdrcP.jpg" mos="" align="middle" fullscreen="" width="679" height="648" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0FLYJ56H9?" target="_blank" rel="nofollow" data-dimension112="a2f94506-94c7-11f1-96bb-69dd07576d6d" data-action="Deal Block" data-label="QuietComfort Bluetooth Earbud" data-dimension48="QuietComfort Bluetooth Earbud" data-dimension25="$149">QuietComfort Bluetooth Earbud: was $179 now $149</a></strong><br>The Bose QuietComfort Earbuds (2024) are a fantastic pair of noise-canceling earbuds that don’t break the bank. Bose reigns supreme in terms of sound quality and active noise cancellation, and that’s very much the case with these mid-range earbuds too. You’re getting best-in-class sound quality and ANC — all for just $149.<a class="view-deal button" href="https://www.amazon.com/dp/B0FLYJ56H9?" target="_blank" rel="nofollow" data-dimension112="a2f94506-94c7-11f1-96bb-69dd07576d6d" data-action="Deal Block" data-label="QuietComfort Bluetooth Earbud" data-dimension48="QuietComfort Bluetooth Earbud" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94682-94c7-11f1-a3be-33811d96cd59" data-action="Deal Block" data-label="TV Speaker" data-dimension48="TV Speaker" data-dimension25="$199" href="https://www.amazon.com/dp/B088KRPCQJ/" 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:39.47%;"><img id="AHTTmLHs5odVXSuSHG9SB9" name="TV speaker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/AHTTmLHs5odVXSuSHG9SB9.png" mos="" align="middle" fullscreen="" width="1500" height="592" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This budget soundbar will bring extra dialogue clarity to make sure you can hear every word of commentary. It's also louder than your TV speakers, so you can crank it up and enjoy background music to the max.<a class="view-deal button" href="https://www.amazon.com/dp/B088KRPCQJ/" target="_blank" rel="nofollow" data-dimension112="a2f94682-94c7-11f1-a3be-33811d96cd59" data-action="Deal Block" data-label="TV Speaker" data-dimension48="TV Speaker" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="a2f946f0-94c7-11f1-ba21-6b05d32c7298" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1268px;"><p class="vanilla-image-block" style="padding-top:62.38%;"><img id="qDBpkjESq4Z3UsvHc4vEeX" name="bose open earbuds" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qDBpkjESq4Z3UsvHc4vEeX.jpg" mos="" align="middle" fullscreen="" width="1268" height="791" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="a2f946f0-94c7-11f1-ba21-6b05d32c7298" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">Ultra Open Earbuds: was $299 now $199</a></strong><br>The Bose Ultra Open perfected the open earbud formula. They have a comfortable, non-invasive fit, solid battery life, and they sound better than you might expect from open buds. They got <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds">4 stars in our review</a>, where we loved their simple controls and surprising lack of sound leakage. <a class="view-deal button" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="a2f946f0-94c7-11f1-ba21-6b05d32c7298" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94768-94c7-11f1-a805-473bc7c2c47f" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$229" href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0CCZ26B5V" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:134.46%;"><img id="hHbg269p5MJKe2LQvoMyMd" name="bose quietcomfort" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/hHbg269p5MJKe2LQvoMyMd.jpg" mos="" align="middle" fullscreen="" width="679" height="913" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In our <a href="https://www.tomsguide.com/reviews/bose-quietcomfort-headphones" data-dimension112="a2f94768-94c7-11f1-a805-473bc7c2c47f" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$229">Bose QuietComfort Headphones review</a>, we said these are "excellent always-on noise-cancelers with enhanced sound and battery life." They're missing spatial audio mode and touch controls, but for $130 less, these are nearly perfect. <a class="view-deal button" href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0CCZ26B5V" target="_blank" rel="nofollow" data-dimension112="a2f94768-94c7-11f1-a805-473bc7c2c47f" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$229">View Deal</a></p></div><div class="product"><a data-dimension112="a2f947d6-94c7-11f1-954a-f75bf6fac0e9" data-action="Deal Block" data-label="QuietComfort Ultra Earbuds gen 2" data-dimension48="QuietComfort Ultra Earbuds gen 2" data-dimension25="$249" href="https://www.amazon.com/dp/B0F7M3HPBD?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:934px;"><p class="vanilla-image-block" style="padding-top:124.20%;"><img id="a9jV9Xm3AhpGwNWnqABVVP" name="Bose QuietComfort Ultra Earbuds gen 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/a9jV9Xm3AhpGwNWnqABVVP.png" mos="" align="middle" fullscreen="" width="934" height="1160" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Earbuds gen 2 bring some big improvements to the table. They fit even better than the outgoing model, contain even better ANC, and there are increased sound modes for even more immersive listening. Six hours of battery might not be earth-shattering, but you can now charge the case wirelessly.<a class="view-deal button" href="https://www.amazon.com/dp/B0F7M3HPBD?" target="_blank" rel="nofollow" data-dimension112="a2f947d6-94c7-11f1-954a-f75bf6fac0e9" data-action="Deal Block" data-label="QuietComfort Ultra Earbuds gen 2" data-dimension48="QuietComfort Ultra Earbuds gen 2" data-dimension25="$249">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94844-94c7-11f1-8b98-61285541e2a6" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$349" href="https://www.amazon.com/dp/B0D4ZC4XMT?" 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="g64SDV3bmD2tcgopiiNgP6" name="Bose QuietComfort Ultra headphones Versus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/g64SDV3bmD2tcgopiiNgP6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Headphones get you epic ANC, super sound, and the most comfortable fit this side of wrapping your head in a snood that plays music. 24 hours of battery life isn't the best, but they make up for it with just about everything else.<a class="view-deal button" href="https://www.amazon.com/dp/B0D4ZC4XMT?" target="_blank" rel="nofollow" data-dimension112="a2f94844-94c7-11f1-8b98-61285541e2a6" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$349">View Deal</a></p></div><div class="product"><a data-dimension112="a2f948b2-94c7-11f1-90c7-b9959623daa5" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$309" href="https://www.amazon.com/dp/B0CVL1K7DX/" 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="dT2cC2J8saDETsZvrBwfqk" name="Bose_SoundLink_Max_square.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/dT2cC2J8saDETsZvrBwfqk.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>With big sound and a fashionable look, the SoundLink Max is a great portable companion for taking on the go. 20 hours of battery life is a big bonus as well, giving you plenty of listening time. <a class="view-deal button" href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow" data-dimension112="a2f948b2-94c7-11f1-90c7-b9959623daa5" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$309">View Deal</a></p></div><h3 class="article-body__section" id="section-shop-more-deals"><span>Shop More Deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Headphones&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/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/audio/i-found-9-bose-deals-on-amazon-that-are-actually-worth-your-money-up-to-36-percent-off-right-now</link>
                                                                            <description>
                            <![CDATA[ Amazon is knocking up to 36% off Bose speakers and headphones. Check out my favorite picks from the sale. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">XrEtKLdZKeaxdiyiVZoszW</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/B5XvHVAkvArsDunVyieL2S-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 10 Aug 2026 14:55:22 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Olivia Halevy ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/YAg7Fmh9dGKbg3hULBMWiU.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Olivia is a Deals Writer at Tom’s Guide. She loves all things lifestyle, shopping and sourcing the best deals from some of the top brands, retailers and websites around. From fashion and beauty to smart home and tech, she has an eye for finding practical, stylish, and reasonably-priced items that help make life simpler (and a little more aesthetically-pleasing).&amp;nbsp;Olivia has over five years of experience writing lifestyle, commerce, and branded content for &lt;a href=&quot;http://patch.com/&quot;&gt;Patch.com&lt;/a&gt;. She&#039;s covered major shopping holidays like Amazon Prime Day, Black Friday and Cyber Monday. In her free time, she enjoys trying out new exercise classes, binge-watching the latest viral TV shows, cooking, spending time with family and taking long walks with her dog, Gus.&amp;nbsp;&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/B5XvHVAkvArsDunVyieL2S-1280-80.jpg">
                                                            <media:credit><![CDATA[Amazon / edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Bose]]></media:description>                                                            <media:text><![CDATA[Bose]]></media:text>
                                <media:title type="plain"><![CDATA[Bose]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/B5XvHVAkvArsDunVyieL2S-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Bose makes some of the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best headphones</a> and <a href="https://www.tomsguide.com/best-picks/best-waterproof-speakers">best waterproof Bluetooth speakers</a> on the market. If you're in the market for some new gear from the popular audio brand you're in luck: Amazon is currently knocking up to 36% off Bose devices. </p><p>Looking for a new speaker? I recommend <a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow">the Bose SoundLink Flex (Gen 2) for just $119</a>. When it comes to headphones, you can't go wrong with the <a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0CCZ26B5V" target="_blank" rel="nofollow">Bose QuietComfort Headphones for $229</a>, which are currently $130 off. </p><p>We don't know how long these rare Bose deals will last, so we recommending adding to cart ASAP. Keep scrolling to check out the rest of my favorite picks from the sale. </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links </span></h3><ul><li><a href="https://www.amazon.com/stores/Bose/page/9E24AFD5-B420-4859-BD2F-A993BA579B20?" target="_blank" rel="nofollow"><strong>shop all Bose deals on Amazon</strong></a></li><li><strong>Bose SoundLink Micro: </strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FF4RY23S/" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Bose SoundLink Flex (Gen 2): </strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow"><strong>was $149 now $119</strong></a></li><li><strong>Bose QuietComfort Bluetooth Earbud: </strong><a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Lifestyle-Cancellation/dp/B0D8BWD76H" target="_blank" rel="nofollow"><strong>was $179 now $149</strong></a></li><li><strong>Bose Ultra Open Earbuds: </strong><a href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><strong>was $299 now $199</strong></a></li><li><strong>Bose QuietComfort Headphones: </strong><a href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0CCZ26B5V" target="_blank" rel="nofollow"><strong>was $349 now $229</strong></a></li><li><strong>Bose SoundLink Max: </strong><a href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow"><strong>was $399 now $309</strong></a></li></ul><h3 class="article-body__section" id="section-best-bose-deals"><span>Best Bose Deals</span></h3><div class="product"><a data-dimension112="a2f9442a-94c7-11f1-a785-91173c6eac9d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FF4RY23S/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1105px;"><p class="vanilla-image-block" style="padding-top:109.50%;"><img id="jBQUxpMSaZYZyyD2LJmGD7" name="Bose Micro" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jBQUxpMSaZYZyyD2LJmGD7.jpg" mos="" align="middle" fullscreen="" width="1105" height="1210" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FF4RY23S/" target="_blank" rel="nofollow" data-dimension112="a2f9442a-94c7-11f1-a785-91173c6eac9d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99">SoundLink Micro Portable Bluetooth Speaker: was $129 now $99</a></strong><br>The SoundLink Micro is a compact, portable speaker you can take on the road. The Bluetooth speaker is waterproof (IPX7 certified) and offers up to 6 hours of play time off a single charge. It can also be paired with another SoundLink speaker to create a stereo mode.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FF4RY23S/" target="_blank" rel="nofollow" data-dimension112="a2f9442a-94c7-11f1-a785-91173c6eac9d" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94498-94c7-11f1-81aa-a386e4e4cdd5" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:75.11%;"><img id="fexRzMYRvYZTHGfbcUbFf8" name="Bose speaker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/fexRzMYRvYZTHGfbcUbFf8.jpg" mos="" align="middle" fullscreen="" width="679" height="510" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow" data-dimension112="a2f94498-94c7-11f1-81aa-a386e4e4cdd5" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119">SoundLink Flex (Gen 2): was $149 now $119</a></strong><br>The new Bose SoundLink (2nd gen) is a compact and powerful Bluetooth speaker with a durable design. This 1.2-pound speaker is water and dust proof and delivered great sound quality in our <a href="https://www.tomsguide.com/audio/bose-soundlink-flex-2-review" target="_blank">SoundLink Flex gen 2 review</a>. With 12 hours of battery life and $30 off, you have yourself a great deal.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow" data-dimension112="a2f94498-94c7-11f1-81aa-a386e4e4cdd5" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94506-94c7-11f1-96bb-69dd07576d6d" data-action="Deal Block" data-label="QuietComfort Bluetooth Earbud" data-dimension48="QuietComfort Bluetooth Earbud" data-dimension25="$149" href="https://www.amazon.com/dp/B0FLYJ56H9?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:95.43%;"><img id="tEtgPEcP9tofgugi8QdrcP" name="Bose quietcomfort" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/tEtgPEcP9tofgugi8QdrcP.jpg" mos="" align="middle" fullscreen="" width="679" height="648" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0FLYJ56H9?" target="_blank" rel="nofollow" data-dimension112="a2f94506-94c7-11f1-96bb-69dd07576d6d" data-action="Deal Block" data-label="QuietComfort Bluetooth Earbud" data-dimension48="QuietComfort Bluetooth Earbud" data-dimension25="$149">QuietComfort Bluetooth Earbud: was $179 now $149</a></strong><br>The Bose QuietComfort Earbuds (2024) are a fantastic pair of noise-canceling earbuds that don’t break the bank. Bose reigns supreme in terms of sound quality and active noise cancellation, and that’s very much the case with these mid-range earbuds too. You’re getting best-in-class sound quality and ANC — all for just $149.<a class="view-deal button" href="https://www.amazon.com/dp/B0FLYJ56H9?" target="_blank" rel="nofollow" data-dimension112="a2f94506-94c7-11f1-96bb-69dd07576d6d" data-action="Deal Block" data-label="QuietComfort Bluetooth Earbud" data-dimension48="QuietComfort Bluetooth Earbud" data-dimension25="$149">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94682-94c7-11f1-a3be-33811d96cd59" data-action="Deal Block" data-label="TV Speaker" data-dimension48="TV Speaker" data-dimension25="$199" href="https://www.amazon.com/dp/B088KRPCQJ/" 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:39.47%;"><img id="AHTTmLHs5odVXSuSHG9SB9" name="TV speaker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/AHTTmLHs5odVXSuSHG9SB9.png" mos="" align="middle" fullscreen="" width="1500" height="592" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This budget soundbar will bring extra dialogue clarity to make sure you can hear every word of commentary. It's also louder than your TV speakers, so you can crank it up and enjoy background music to the max.<a class="view-deal button" href="https://www.amazon.com/dp/B088KRPCQJ/" target="_blank" rel="nofollow" data-dimension112="a2f94682-94c7-11f1-a3be-33811d96cd59" data-action="Deal Block" data-label="TV Speaker" data-dimension48="TV Speaker" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="a2f946f0-94c7-11f1-ba21-6b05d32c7298" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1268px;"><p class="vanilla-image-block" style="padding-top:62.38%;"><img id="qDBpkjESq4Z3UsvHc4vEeX" name="bose open earbuds" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qDBpkjESq4Z3UsvHc4vEeX.jpg" mos="" align="middle" fullscreen="" width="1268" height="791" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="a2f946f0-94c7-11f1-ba21-6b05d32c7298" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">Ultra Open Earbuds: was $299 now $199</a></strong><br>The Bose Ultra Open perfected the open earbud formula. They have a comfortable, non-invasive fit, solid battery life, and they sound better than you might expect from open buds. They got <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds">4 stars in our review</a>, where we loved their simple controls and surprising lack of sound leakage. <a class="view-deal button" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="a2f946f0-94c7-11f1-ba21-6b05d32c7298" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94768-94c7-11f1-a805-473bc7c2c47f" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$229" href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0CCZ26B5V" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:134.46%;"><img id="hHbg269p5MJKe2LQvoMyMd" name="bose quietcomfort" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/hHbg269p5MJKe2LQvoMyMd.jpg" mos="" align="middle" fullscreen="" width="679" height="913" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In our <a href="https://www.tomsguide.com/reviews/bose-quietcomfort-headphones" data-dimension112="a2f94768-94c7-11f1-a805-473bc7c2c47f" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$229">Bose QuietComfort Headphones review</a>, we said these are "excellent always-on noise-cancelers with enhanced sound and battery life." They're missing spatial audio mode and touch controls, but for $130 less, these are nearly perfect. <a class="view-deal button" href="https://www.amazon.com/Bose-QuietComfort-Cancelling-Headphones-Bluetooth/dp/B0CCZ26B5V" target="_blank" rel="nofollow" data-dimension112="a2f94768-94c7-11f1-a805-473bc7c2c47f" data-action="Deal Block" data-label="QuietComfort Headphones" data-dimension48="QuietComfort Headphones" data-dimension25="$229">View Deal</a></p></div><div class="product"><a data-dimension112="a2f947d6-94c7-11f1-954a-f75bf6fac0e9" data-action="Deal Block" data-label="QuietComfort Ultra Earbuds gen 2" data-dimension48="QuietComfort Ultra Earbuds gen 2" data-dimension25="$249" href="https://www.amazon.com/dp/B0F7M3HPBD?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:934px;"><p class="vanilla-image-block" style="padding-top:124.20%;"><img id="a9jV9Xm3AhpGwNWnqABVVP" name="Bose QuietComfort Ultra Earbuds gen 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/a9jV9Xm3AhpGwNWnqABVVP.png" mos="" align="middle" fullscreen="" width="934" height="1160" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Earbuds gen 2 bring some big improvements to the table. They fit even better than the outgoing model, contain even better ANC, and there are increased sound modes for even more immersive listening. Six hours of battery might not be earth-shattering, but you can now charge the case wirelessly.<a class="view-deal button" href="https://www.amazon.com/dp/B0F7M3HPBD?" target="_blank" rel="nofollow" data-dimension112="a2f947d6-94c7-11f1-954a-f75bf6fac0e9" data-action="Deal Block" data-label="QuietComfort Ultra Earbuds gen 2" data-dimension48="QuietComfort Ultra Earbuds gen 2" data-dimension25="$249">View Deal</a></p></div><div class="product"><a data-dimension112="a2f94844-94c7-11f1-8b98-61285541e2a6" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$349" href="https://www.amazon.com/dp/B0D4ZC4XMT?" 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="g64SDV3bmD2tcgopiiNgP6" name="Bose QuietComfort Ultra headphones Versus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/g64SDV3bmD2tcgopiiNgP6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Headphones get you epic ANC, super sound, and the most comfortable fit this side of wrapping your head in a snood that plays music. 24 hours of battery life isn't the best, but they make up for it with just about everything else.<a class="view-deal button" href="https://www.amazon.com/dp/B0D4ZC4XMT?" target="_blank" rel="nofollow" data-dimension112="a2f94844-94c7-11f1-8b98-61285541e2a6" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$349">View Deal</a></p></div><div class="product"><a data-dimension112="a2f948b2-94c7-11f1-90c7-b9959623daa5" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$309" href="https://www.amazon.com/dp/B0CVL1K7DX/" 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="dT2cC2J8saDETsZvrBwfqk" name="Bose_SoundLink_Max_square.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/dT2cC2J8saDETsZvrBwfqk.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>With big sound and a fashionable look, the SoundLink Max is a great portable companion for taking on the go. 20 hours of battery life is a big bonus as well, giving you plenty of listening time. <a class="view-deal button" href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow" data-dimension112="a2f948b2-94c7-11f1-90c7-b9959623daa5" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$309">View Deal</a></p></div><h3 class="article-body__section" id="section-shop-more-deals"><span>Shop More Deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Headphones&view_mode=carousel&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=light_blue" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vc2hvcC1hc3NldHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Nob3AtYXNzZXRzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9zaG9wLWFzc2V0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/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[ Tom's Guide TV & Audio Awards 2026: 25 new products that take your home theater to the next level ]]></title>
                                                                                                <dc:content><![CDATA[ <p>We’re just over half way through 2026 and it’s shaping up to be one of the best years yet for home cinema enthusiasts. We’ve seen new, cutting-edge TV technology in the form of RGB TVs from Samsung, LG, Sony and Hisense; fantastic active speakers from Klipsch and Edifier; and new luxurious high-end open-back headphones that have blown us away. </p><p>To help you sort through this year’s cavalcade of great gear, we’ve put together a list of 25 award-winning products that deserve a spot in your home theater or listening room. This list of products is sorted in alphabetical order to avoid any bias or claims of favoritism, so be sure to scroll all the way down to see the complete list of winners. </p><h3 class="article-body__section" id="section-tvs"><span>TVs</span></h3><h2 id="samsung-s95h-oled">Samsung S95H OLED</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="J6ffYFsUDK4FRr9DgCrnt9" name="Samsung-S95H--9" alt="Samsung S95H OLED TV on a desk" src="https://cdn.mos.cms.futurecdn.net/J6ffYFsUDK4FRr9DgCrnt9.jpg" mos="" align="middle" fullscreen="" width="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 <a href="https://www.tomsguide.com/tvs/oled-tvs/samsung-s95h-oled-tv-review">Samsung S95H OLED TV</a> earned my 5/5 star recommendation because it sets a completely new benchmark for OLED performance. It delivers fantastic brightness that confidently challenges traditional LED sets without compromising the sophistication of black levels. Thanks to a Glare Free display, the TV looks spectacular in both dark home theaters and bright living rooms. It even features fantastic gaming capabilities with low input lag right out of the box. <em>— Kate Kozuch, Managing Editor</em></p><div data-widget-type="merchantlink" data-model-name="Samsung S95H"></div><h2 id="sony-bravia-9-ii-true-rgb-tv">Sony BRAVIA 9 II TRUE RGB 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="Qq5UXP4tsoum73NSQ4yqjH" name="Sony-Bravia-9-II--3" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/Qq5UXP4tsoum73NSQ4yqjH.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 <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-9-ii-true-rgb-tv-review">Sony Bravia 9 II</a> True RGB TV is an absolute masterclass in modern LED technology and is, undisputedly, the champion of the current RGB display revolution. I recommend it for its astounding luminance and virtually invisible blooming, getting as close to OLED I’ve ever seen in terms of black levels. Delivering nearly 4,000 nits peak HDR brightness on a glare-reducing screen, this TV is the ultimate premium choice for bright room viewing. <em>— Kate Kozuch, Managing Editor</em></p><h2 id="tcl-qm8l-sqd-mini-led-tv">TCL QM8L SQD-Mini LED 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="93RwbkSjAhiy9o2vwMM84h" name="TCL-75QM8L-TV-3" alt="A TCL QM8L TV on a desk" src="https://cdn.mos.cms.futurecdn.net/93RwbkSjAhiy9o2vwMM84h.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 the rest of the industry is zigging toward RGB technology, TCL is sticking to white light-driven Mini-LED and ratcheting everything up to 11. SQD stands for Super Quantum Dot, and the new <a href="https://www.tomsguide.com/tvs/qled-tvs/tcl-sqd-mini-led-qm8l-75-inch-tv-review">QM8L</a> uses it to deliver high levels of brightness and high levels of color without breaking the bank. I saw it at CES 2026 and knew it was going to be the mid-range LED to beat this year. <em>— Nick Pino, Managing Editor </em></p><div data-widget-type="merchantlink" data-model-name="TCL QM8L"></div><h3 class="article-body__section" id="section-streaming-stick"><span>Streaming Stick</span></h3><h2 id="roku-streaming-stick-plus">Roku Streaming Stick 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YMYnm9u6tFZf4DYNwEXnaa" name="Roku Streaming Stick Plus-2" alt="Roku Streaming Stick Plus" src="https://cdn.mos.cms.futurecdn.net/YMYnm9u6tFZf4DYNwEXnaa.jpg" mos="" align="middle" fullscreen="" width="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 <a href="https://www.tomsguide.com/entertainment/streaming-devices/roku-streaming-stick-plus-review-a-slimmed-down-4k-hdr-powerhouse">Roku Streaming Stick Plus</a> nails the basics: It’s 4K HDR streaming in a pint-sized package for under $50. Roku’s primary interface is one of the easiest to use (and has been for years) but still features pretty much every single streaming service you’d want to watch. Streaming sticks may not be the home theater staples they once were, but anyone interested in adding new functionality to an aging 4K HDR TV will appreciate Roku’s slim and affordable solution. <em>— Nick Pino, Managing Editor </em></p><div data-widget-type="merchantlink" data-model-name="Roku Streaming Stick Plus"></div><h3 class="article-body__section" id="section-projectors"><span>Projectors</span></h3><h2 id="hisense-xr10-4k-triple-laser-projector">Hisense XR10 4K Triple Laser Projector</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="sfCCe4yZcwHdDLvBnTuHpE" name="Hisense XR10 4K Triple Laser Projector-2" alt="The Hisense XR10 on a pedestal" src="https://cdn.mos.cms.futurecdn.net/sfCCe4yZcwHdDLvBnTuHpE.jpg" mos="" align="middle" fullscreen="" width="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 Hisense XR10 is an exceptional projector with a bright, crystal-clear image and contrast I’ve rarely seen from projectors. It’s brilliant as a home theater beamer, it rocks for games, and it’s easily adaptable to just about any space. The Hisense XR10 offers simply brilliant projection that’s a knockout in dark home theater environments and can still dazzle even with overhead lights on.<em> — Mark Knapp, Tom’s Guide Contributor</em></p><div data-widget-type="merchantlink" data-model-name="Hisense XR10"></div><h2 id="jmgo-n3-ultimate">JMGO N3 Ultimate</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1110px;"><p class="vanilla-image-block" style="padding-top:56.31%;"><img id="SbhM6Q8frRftaBts6ia2PP" name="01-KV-2928-1200-PC" alt="The JMGO N3 Ultimate on a coffee table in a home theater." src="https://cdn.mos.cms.futurecdn.net/SbhM6Q8frRftaBts6ia2PP.jpg" mos="" align="middle" fullscreen="" width="1110" height="625" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: JMGO)</span></figcaption></figure><p>In the early part of my career I avoided portable projectors like the plague. I found their performance lackluster and, despite their claims about being user friendly, never quite worked the way I wanted them to. The JMGO N3 Ultimate delivers the convenience and performance that I craved all those years ago. I'm definitely getting one of these for myself. <em>— Nick Pino, Managing Editor</em></p><h3 class="article-body__section" id="section-soundbars"><span>Soundbars</span></h3><h2 id="bluesound-pulse-cinema">Bluesound Pulse Cinema</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1176px;"><p class="vanilla-image-block" style="padding-top:56.21%;"><img id="qioXZSdaYJtwZE2zdYy4D4" name="Cinema_Lifestyle_3_1ee3d7f7-f46e-4a48-b92a-4fb1815b854a" alt="The Bluesound Pulse Cinema on an entertainment center." src="https://cdn.mos.cms.futurecdn.net/qioXZSdaYJtwZE2zdYy4D4.jpg" mos="" align="middle" fullscreen="" width="1176" height="661" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Bluesound)</span></figcaption></figure><p>If you've yet to try a BluOS product, I urge you to do so. There are plenty of multi-room audio solutions on the market, but Bluesound's is the best by far. Its latest soundbar not only uses that platform to great effect, but it delivers 3.2.2 Dolby Atmos sound via 16 drivers. It's a next-level soundbar from a company on the cutting-edge of sound performance. <em>— Nick Pino, Managing Editor</em></p><h2 id="samsung-hw-qs90h-soundbar">Samsung HW-QS90H Soundbar</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1120px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8NHTvBPSVMEhpYAuZZFtHb" name="Samsung HW-Q90H" alt="The Samsung HW-QS90H Soundbar on a black gradient background." src="https://cdn.mos.cms.futurecdn.net/8NHTvBPSVMEhpYAuZZFtHb.jpg" mos="" align="middle" fullscreen="" width="1120" height="630" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Samsung)</span></figcaption></figure><p>Want a soundbar that can seriously do it all? The Samsung QS90H is your one to watch. This all-in-one soundbar combines a powerful subwoofer with seven center channels and two surrounds — yes, in one bar. Dialogue is crystal-clear and smooth, and diegetic sounds are exceptionally detailed. It’s ideal for home setups that might not have space for a dedicated sub or rears. — <em>Erin Bashford, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="Samsung HW-QS90H"></div><h3 class="article-body__section" id="section-headphones"><span>Headphones</span></h3><h2 id="audeze-maxwell-2-anc">Audeze Maxwell 2 ANC</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4xHjavBtZVgQW3GbHynR3i" name="Audeze_Maxwell2_.JPG" alt="Audeze Maxwell 2 against a blue background" src="https://cdn.mos.cms.futurecdn.net/4xHjavBtZVgQW3GbHynR3i.jpg" 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>By adding noise cancellation to the already fantastic Maxwell 2 formula, the new <a href="https://www.tomsguide.com/gaming/gaming-peripherals/audeze-maxwell-2-review">Maxwell 2</a> is the all-rounder that Audeze’s gaming lineup has been crying out for. Its planar magnetic drivers deliver excellent fidelity, and while there are other headsets out there using similar drivers, none offer the immersion of ANC and a closed back design. — <em>Pete Wolinski, Senior Reviews Editor</em></p><div data-widget-type="merchantlink" data-model-name="Audeze Maxwell 2 ANC"></div><h2 id="belkin-soundform-mini-2-kids-wireless-headphones">Belkin SoundForm Mini 2 Kids Wireless Headphones</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1098px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="aiLPQYCSgkocT4e8wTUUJJ" name="7109uts+gUL._AC_SL1500_" alt="The Belkin SoundForm Mini 2 Kids Wireless Headphones on a child's head." src="https://cdn.mos.cms.futurecdn.net/aiLPQYCSgkocT4e8wTUUJJ.jpg" mos="" align="middle" fullscreen="" width="1098" height="618" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Belkin)</span></figcaption></figure><p>As a relatively new dad, I take my daughter’s health seriously. As desperate as I am to have her right beside me listening to music on the latest speakers and soundbars, I want to preserve her hearing health — that’s where these cheap wireless headphones come in. With a set volume cap, there’s no way for her to crank up the music to a dangerously high level, which makes me feel better about the times we do listen to music together. <em>— Nick Pino, Managing Editor</em></p><h2 id="jbl-live-780nc">JBL LIVE 780NC</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7YxFuTJ3bm4gYmtkcWDmLN" name="JBL Live 780NC 4 2.JPG" alt="the jbl live 780NC in orange photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/7YxFuTJ3bm4gYmtkcWDmLN.jpg" 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>Bolder doesn’t always mean better, but in the case of the <a href="https://www.tomsguide.com/audio/headphones/jbl-live-780nc-review">JBL Live 780NC</a>, it does. Available in a splash of striking colors, including the bright orange we tested, the Live 780NC combine style, finesse, and good vibes. Sound quality is just as vibrant as the design, and the battery life is good enough to keep you movin’ and groovin’ all week long. — <em>Erin Bashford, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="JBL Live 780NC"></div><h2 id="meze-audio-strada">Meze Audio STRADA</h2><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="kcg5ZWzvrxaST6eNqhxi3b" name="Meze-Audio-STRADA-headphone-04" alt="The Meze Audio Strada on a black background." src="https://cdn.mos.cms.futurecdn.net/kcg5ZWzvrxaST6eNqhxi3b.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: Meze)</span></figcaption></figure><p>Audiophiles are quick to say that open-back headphones deliver a more natural, concert hall-style performance, but if you want lower bass response, you're not going to get it in open-backs. The closed-back Meze Audio STRADA delivers that clean, crisp low-end performance in spades. For high-end headphones, they're actually pretty affordable, too. <em>— Nick Pino, Managing Editor</em></p><div data-widget-type="merchantlink" data-model-name="Meze Audio Strada"></div><h3 class="article-body__section" id="section-earbuds"><span>Earbuds</span></h3><h2 id="earfun-clip-2">EarFun Clip 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Gw563LUsUGqRm9BbhHPAja" name="EarFun Clip 2" alt="EarFun Clip 2 in black on table photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/Gw563LUsUGqRm9BbhHPAja.jpg" 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 gym-goers, runners and anyone who wants to remain aware while working out, few open-earbuds are better than the <a href="https://www.tomsguide.com/audio/earbuds/earfun-clip-2-review"><u>EarFun Clip 2</u></a>. Their comfortable clip style is a winner in our books, and they’ve got AI-powered real-time translation to boot. Generally good for listening to music as well as taking calls, we were also impressed by the Clip 2’s 40-hour battery life. They’re some of the best cheap earbuds you can buy right now. — <em>Nikita Achanta, Senior Writer Reviews</em></p><h2 id="shokz-opendots-2">ShokzOpenDots 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="X3umr7xjpMtLmQUkc5LCdP" name="Shokz OpenDots 2--1-LIST" alt="Shokz OpenDots 2" src="https://cdn.mos.cms.futurecdn.net/X3umr7xjpMtLmQUkc5LCdP.jpg" mos="" align="middle" fullscreen="" width="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 <a href="https://www.tomsguide.com/audio/headphones/shokz-opendots-2-review">Shokz OpenDots 2</a> offer an impressive combination of a great fit, excellent sound quality for open buds, and long battery life. They’re ideal for runs, strength sessions and other workouts, and great for general use as well, keeping you aware of your surroundings while delivering better sound than bone conduction headphones. If you’ve yet to try clip-on headphones, the OpenDots 2 will convince you of the advantages of the design. <em>— Nick Harris-Fry, Senior Fitness Writer</em></p><div data-widget-type="merchantlink" data-model-name="Shokz OpenDots 2"></div><h2 id="technics-eah-az100-wireless-earbuds">Technics EAH-AZ100 Wireless Earbuds</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JpZWSQFwk6uZZAogPxQb2d" name="Technics EAH-AZ100 Wireless Earbuds-1-LIST" alt="Technics EAH-AZ100 Wireless Earbuds with case" src="https://cdn.mos.cms.futurecdn.net/JpZWSQFwk6uZZAogPxQb2d.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In the pantheon of the best wireless earbuds makers, Technics holds a top spot. The Technics EAH-AZ80 that debuted in 2023 are some of my favorite earbuds of the last 10 years, and the new <a href="https://www.tomsguide.com/audio/earbuds/technics-eah-az100-wireless-earbuds-review">Technics EAH-AZ100</a> are even better. For discerning listeners looking for a pair of wireless earbuds, the Technics EAH-AZ100 deliver class-leading sound with head-tracked Dolby Atmos audio. For $249, I definitely recommend them. <em>— Nick Pino, Managing Editor </em></p><div data-widget-type="merchantlink" data-model-name="Technics EAH-AZ100 Wireless Earbuds"></div><h3 class="article-body__section" id="section-speakers"><span>Speakers</span></h3><h2 id="creative-technology-xf1">Creative Technology XF1</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:755px;"><p class="vanilla-image-block" style="padding-top:53.25%;"><img id="R4TAEmu9CQMTPGCDwkVA9k" name="1" alt="The Creative Technology XF1 desktop speakers on a desk." src="https://cdn.mos.cms.futurecdn.net/R4TAEmu9CQMTPGCDwkVA9k.jpg" mos="" align="middle" fullscreen="" width="755" height="402" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Creative)</span></figcaption></figure><p>It's not often that I get excited about computer speakers. If I'm sitting at my desk, I'm usually testing out a new pair of awesome open-back headphones or I'm going to connect wirelessly to a nearby pair of bookshelf speakers. But the Creative XF1 has me intrigued, for sure. With a 10-band parametric equalizer and more than 120 genre- and game-specific presets, it's the Swiss Army knife of desktop speakers. <em>— Nick Pino, Managing Editor</em></p><h2 id="edifier-s880db-mkii">Edifier S880DB MKII</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YRQ7UVexseHyZKTjrzepLa" name="Edifier_S880DB_MK2_ 1.JPG" alt="the edifier s880db mkii speakers in white and wood colorway showing the connectivity options, remote control, against a blue background" src="https://cdn.mos.cms.futurecdn.net/YRQ7UVexseHyZKTjrzepLa.jpg" 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>There’s a reason why the audio world made such a racket about the <a href="https://www.tomsguide.com/audio/speakers/edifier-s880db-mkii-review">Edifier S880DB MKII</a> upon release late last year. These $449 speakers look like a high-end brand and the sound matches up, too. With a rich, controlled low-end, expansive mids, and a dextrous, flexible treble, the S880DB MKII are some of the best midrange bookshelf speakers available right now. — <em>Erin Bashford, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="Edifier S880DB MKII"></div><h2 id="klipsch-the-nines-ii-powered-speakers">Klipsch The Nines II Powered Speakers</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:793px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="G9KURrBgud8Cmpqv4S94vR" name="84b4a063-8ed2-47e2-b068-5cee6b88d8d5" alt="Klipsch's The Nines II in a living room." src="https://cdn.mos.cms.futurecdn.net/G9KURrBgud8Cmpqv4S94vR.jpg" mos="" align="middle" fullscreen="" width="793" height="446" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Klipsch)</span></figcaption></figure><p>Klipsch's The Nines II were my favorite speakers of CES 2026. I heard them in a small enclosed booth on the Las Vegas Convention Center and I instantly fell in love with them. Despite their medium-sized footprint, they're bombastic. They deliver powerful, detailed sound with a fairly wide soundstage. It also comes with Dirac Live Room Correction that measures the acoustic characteristics of your room and automatically tunes the sound to prevent issues. I'm here for it. <em>— Nick Pino, Managing Editor</em></p><div data-widget-type="merchantlink" data-model-name="Klipsch The Nines II"></div><h2 id="svs-3000-micro-r-evolution-subwoofer">SVS 3000 Micro R|Evolution Subwoofer</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:867px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="8eWva2gMLdJD2wmFQDaeVJ" name="SVS 3000 Micro" alt="The SVS 3000 Micro Subwoofer on the floor in a room." src="https://cdn.mos.cms.futurecdn.net/8eWva2gMLdJD2wmFQDaeVJ.jpg" mos="" align="middle" fullscreen="" width="867" height="488" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: SVS)</span></figcaption></figure><p>Squeezing flagship subwoofer performance into a smaller package is no easy task, and yet, somehow SVS succeeded with the new 3000 Micro R|Evolution Subwoofer. Packaged in a compact 11-inch enclosure, the 3000 Micro R|Evolution Subwoofer delivers 20Hz sub-bass response using dual opposing 9-inch active drivers and a 1,200W RMS amplifier. That’s a serious sub all for under $1,000. <em>— Nick Pino, Managing Editor </em></p><h3 class="article-body__section" id="section-bluetooth-speakers"><span>Bluetooth Speakers</span></h3><h2 id="jbl-go-5">JBL Go 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:5508px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6gTtmYbVXuDQbax58Q6bqX" name="JBL-Go-5-op-ed" alt="A JBL Go 5 Bluetooth speaker in blue" src="https://cdn.mos.cms.futurecdn.net/6gTtmYbVXuDQbax58Q6bqX.jpg" mos="" align="middle" fullscreen="" width="5508" height="3098" 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>Good becomes great as JBL has introduced small but meaningful upgrades, making the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/jbl-go-5-review"><u>JBL Go 5</u></a> one of the best Bluetooth speakers of 2026. It's pocketable so you can take it anywhere with you. Even though it's small, we've been impressed by its wide soundstage and just how loud it can get. Its IP68 rating means it's highly waterproof, dustproof and drop-proof too. At just $54, it offers simply unbeatable value for money. — <em>Nikita Achanta, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="JBL Go 5"></div><h2 id="jbl-xtreme-5">JBL Xtreme 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:4956px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="MhmJUtGJcUZLnrZejP3SpU" name="Xtreme5-edit" alt="the jbl xtreme 5 with water splashing on it outside in the park showing its waterproofing" src="https://cdn.mos.cms.futurecdn.net/MhmJUtGJcUZLnrZejP3SpU.jpg" mos="" align="middle" fullscreen="" width="4956" height="2788" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>What a speaker. The <a href="https://www.tomsguide.com/audio/bluetooth-speakers/jbl-xtreme-5-review">JBL Xtreme 5</a> is officially the most durable speaker in the world thanks to its drop-proof, IP68 rating. But hardiness isn’t the only area the Xtreme 5 excels — it’s also a bass beast. Improving on its predecessor’s loud-but-muffled bass performance, the Xtreme 5 brings control, power, and ferocity to the party. The ultimate party speaker of 2026. — <em>Erin Bashford, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="JBL Xtreme 5"></div><h2 id="sonos-play">Sonos 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:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6LaUgHfNuq5PpiqN8tgajQ" name="IMG_6899.JPG" alt="Sonos Play" src="https://cdn.mos.cms.futurecdn.net/6LaUgHfNuq5PpiqN8tgajQ.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> is a highly versatile portable speaker that flawlessly bridges the gap between everyday home use and travel listening. I recommend it for those looking to blend wireless multi-room audio capabilities with everyday Bluetooth convenience. Offering crisp vocals, satisfying bass and an impressive 24-hour battery life, this lightweight speaker delivers incredibly balanced, room-filling sound. It’s the middle ground between the bulkier <a href="https://www.tomsguide.com/reviews/sonos-move-2">Move 2</a> and smaller <a href="https://www.tomsguide.com/audio/bluetooth-speakers/sonos-roam-2-review">Roam 2</a> that Sonos users didn’t know they were waiting for. <em>— Kate Kozuch, Managing Editor</em></p><div data-widget-type="merchantlink" data-model-name="Sonos Play"></div><h3 class="article-body__section" id="section-home-hi-fi"><span>Home Hi-Fi</span></h3><h2 id="denon-avr-x2900h">Denon AVR-X2900H</h2><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="qms4cDGiCotHddjR3rm9XR" name="PDP-4" alt="The Denon AVR-X2900H on an entertainment center." src="https://cdn.mos.cms.futurecdn.net/qms4cDGiCotHddjR3rm9XR.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: Denon)</span></figcaption></figure><p>Denon knows what it’s doing when it comes to home cinema amplification. The company has been at it for decades, but blimey, this AV receiver is a bit of a belter. For a mid-range model, it’s exceptional. Movies sound convincingly cinematic, and music is rewardingly rhythmic. Thoughtful engineering, sensible connectivity and an excellent multiroom platform are the icing on the cake. <em>— Steve May, Tom’s Guide Contributor</em></p><div data-widget-type="merchantlink" data-model-name="Denon AVR-X2900H"></div><h2 id="fluance-rt87-reference-high-fidelity-dual-plinth-turntable">Fluance RT87 Reference High Fidelity Dual-Plinth Turntable</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3FaaNjzcUD4bEyXzHPi6KZ" name="Fluance RT87 turntable-1-LIST" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/3FaaNjzcUD4bEyXzHPi6KZ.jpg" mos="" align="middle" fullscreen="" width="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 love the way that the RT87 looks. The dual plinth design makes it look unlike any other sub-$1000 turntable and lends a commanding presence to the deck. The carbon tonearm is a nice visual touch, and the hefty acrylic platter really brings everything together. In terms of performance, you’d be hard-pressed to find something that sounds better within $300-$500 of the RT-87’s price. <em>— Tammy Rogers, Buying Guide Editor</em></p><div data-widget-type="merchantlink" data-model-name="Fluance RT87"></div><h2 id="wiim-amp-ultra">WiiM Amp Ultra</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:942px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="NdvEjyeEynesWJHCXUtQL8" name="Wiim Amp Ultra" alt="The Wiim Amp Ultra sitting between a speaker and a turntable." src="https://cdn.mos.cms.futurecdn.net/NdvEjyeEynesWJHCXUtQL8.jpg" mos="" align="middle" fullscreen="" width="942" height="530" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Wiim)</span></figcaption></figure><p>Home entertainment centers can only hold so many products. I wish there was space for an amplifier, a streaming music player, speakers, a turntable, a pre-amp and a CD transport on every shelf. Sadly, there's not. But thankfully Wiim is there to reduce the amount of gear we need with its excellent Amp Ultra that combines a streaming player with an amplifier that can deliver up to 100W per channel. For $549, I've yet to find something better.  <em>— Nick Pino, Managing Editor</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/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><li><a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">These are the 5 best TVs we've tested for movies, sports and gaming you can buy right now</a></li><li><a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">Best headphones: Top picks tested by experts</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/tvs/toms-guide-tv-and-audio-awards-2026</link>
                                                                            <description>
                            <![CDATA[ We’ve put together a list of 25 award-winning products that deserve a spot in your home theater or listening room. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">hFkaUYZvLenAFSp3CtHSRA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/MYZjoyoRdgsAZsK2aX9roN-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 10 Aug 2026 04:45:00 +0000</pubDate>                                                                                                                                <updated>Mon, 10 Aug 2026 14:40:21 +0000</updated>
                                                                                                                                            <category><![CDATA[TVs]]></category>
                                                    <category><![CDATA[Audio]]></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.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>
                                                                                                        <dc:contributor><![CDATA[ Tom&#039;s Guide Staff ]]></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/MYZjoyoRdgsAZsK2aX9roN-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Tom&#039;s Guide TV and AV Awards 2026]]></media:description>                                                            <media:text><![CDATA[The Tom&#039;s Guide TV and AV Awards 2026]]></media:text>
                                <media:title type="plain"><![CDATA[The Tom&#039;s Guide TV and AV Awards 2026]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/MYZjoyoRdgsAZsK2aX9roN-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>We’re just over half way through 2026 and it’s shaping up to be one of the best years yet for home cinema enthusiasts. We’ve seen new, cutting-edge TV technology in the form of RGB TVs from Samsung, LG, Sony and Hisense; fantastic active speakers from Klipsch and Edifier; and new luxurious high-end open-back headphones that have blown us away. </p><p>To help you sort through this year’s cavalcade of great gear, we’ve put together a list of 25 award-winning products that deserve a spot in your home theater or listening room. This list of products is sorted in alphabetical order to avoid any bias or claims of favoritism, so be sure to scroll all the way down to see the complete list of winners. </p><h3 class="article-body__section" id="section-tvs"><span>TVs</span></h3><h2 id="samsung-s95h-oled">Samsung S95H OLED</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="J6ffYFsUDK4FRr9DgCrnt9" name="Samsung-S95H--9" alt="Samsung S95H OLED TV on a desk" src="https://cdn.mos.cms.futurecdn.net/J6ffYFsUDK4FRr9DgCrnt9.jpg" mos="" align="middle" fullscreen="" width="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 <a href="https://www.tomsguide.com/tvs/oled-tvs/samsung-s95h-oled-tv-review">Samsung S95H OLED TV</a> earned my 5/5 star recommendation because it sets a completely new benchmark for OLED performance. It delivers fantastic brightness that confidently challenges traditional LED sets without compromising the sophistication of black levels. Thanks to a Glare Free display, the TV looks spectacular in both dark home theaters and bright living rooms. It even features fantastic gaming capabilities with low input lag right out of the box. <em>— Kate Kozuch, Managing Editor</em></p><div data-widget-type="merchantlink" data-model-name="Samsung S95H"></div><h2 id="sony-bravia-9-ii-true-rgb-tv">Sony BRAVIA 9 II TRUE RGB 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="Qq5UXP4tsoum73NSQ4yqjH" name="Sony-Bravia-9-II--3" alt="Sony Bravia 9 II TV on a desk" src="https://cdn.mos.cms.futurecdn.net/Qq5UXP4tsoum73NSQ4yqjH.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 <a href="https://www.tomsguide.com/tvs/4k-tvs/sony-bravia-9-ii-true-rgb-tv-review">Sony Bravia 9 II</a> True RGB TV is an absolute masterclass in modern LED technology and is, undisputedly, the champion of the current RGB display revolution. I recommend it for its astounding luminance and virtually invisible blooming, getting as close to OLED I’ve ever seen in terms of black levels. Delivering nearly 4,000 nits peak HDR brightness on a glare-reducing screen, this TV is the ultimate premium choice for bright room viewing. <em>— Kate Kozuch, Managing Editor</em></p><h2 id="tcl-qm8l-sqd-mini-led-tv">TCL QM8L SQD-Mini LED 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="93RwbkSjAhiy9o2vwMM84h" name="TCL-75QM8L-TV-3" alt="A TCL QM8L TV on a desk" src="https://cdn.mos.cms.futurecdn.net/93RwbkSjAhiy9o2vwMM84h.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 the rest of the industry is zigging toward RGB technology, TCL is sticking to white light-driven Mini-LED and ratcheting everything up to 11. SQD stands for Super Quantum Dot, and the new <a href="https://www.tomsguide.com/tvs/qled-tvs/tcl-sqd-mini-led-qm8l-75-inch-tv-review">QM8L</a> uses it to deliver high levels of brightness and high levels of color without breaking the bank. I saw it at CES 2026 and knew it was going to be the mid-range LED to beat this year. <em>— Nick Pino, Managing Editor </em></p><div data-widget-type="merchantlink" data-model-name="TCL QM8L"></div><h3 class="article-body__section" id="section-streaming-stick"><span>Streaming Stick</span></h3><h2 id="roku-streaming-stick-plus">Roku Streaming Stick 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YMYnm9u6tFZf4DYNwEXnaa" name="Roku Streaming Stick Plus-2" alt="Roku Streaming Stick Plus" src="https://cdn.mos.cms.futurecdn.net/YMYnm9u6tFZf4DYNwEXnaa.jpg" mos="" align="middle" fullscreen="" width="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 <a href="https://www.tomsguide.com/entertainment/streaming-devices/roku-streaming-stick-plus-review-a-slimmed-down-4k-hdr-powerhouse">Roku Streaming Stick Plus</a> nails the basics: It’s 4K HDR streaming in a pint-sized package for under $50. Roku’s primary interface is one of the easiest to use (and has been for years) but still features pretty much every single streaming service you’d want to watch. Streaming sticks may not be the home theater staples they once were, but anyone interested in adding new functionality to an aging 4K HDR TV will appreciate Roku’s slim and affordable solution. <em>— Nick Pino, Managing Editor </em></p><div data-widget-type="merchantlink" data-model-name="Roku Streaming Stick Plus"></div><h3 class="article-body__section" id="section-projectors"><span>Projectors</span></h3><h2 id="hisense-xr10-4k-triple-laser-projector">Hisense XR10 4K Triple Laser Projector</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="sfCCe4yZcwHdDLvBnTuHpE" name="Hisense XR10 4K Triple Laser Projector-2" alt="The Hisense XR10 on a pedestal" src="https://cdn.mos.cms.futurecdn.net/sfCCe4yZcwHdDLvBnTuHpE.jpg" mos="" align="middle" fullscreen="" width="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 Hisense XR10 is an exceptional projector with a bright, crystal-clear image and contrast I’ve rarely seen from projectors. It’s brilliant as a home theater beamer, it rocks for games, and it’s easily adaptable to just about any space. The Hisense XR10 offers simply brilliant projection that’s a knockout in dark home theater environments and can still dazzle even with overhead lights on.<em> — Mark Knapp, Tom’s Guide Contributor</em></p><div data-widget-type="merchantlink" data-model-name="Hisense XR10"></div><h2 id="jmgo-n3-ultimate">JMGO N3 Ultimate</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1110px;"><p class="vanilla-image-block" style="padding-top:56.31%;"><img id="SbhM6Q8frRftaBts6ia2PP" name="01-KV-2928-1200-PC" alt="The JMGO N3 Ultimate on a coffee table in a home theater." src="https://cdn.mos.cms.futurecdn.net/SbhM6Q8frRftaBts6ia2PP.jpg" mos="" align="middle" fullscreen="" width="1110" height="625" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: JMGO)</span></figcaption></figure><p>In the early part of my career I avoided portable projectors like the plague. I found their performance lackluster and, despite their claims about being user friendly, never quite worked the way I wanted them to. The JMGO N3 Ultimate delivers the convenience and performance that I craved all those years ago. I'm definitely getting one of these for myself. <em>— Nick Pino, Managing Editor</em></p><h3 class="article-body__section" id="section-soundbars"><span>Soundbars</span></h3><h2 id="bluesound-pulse-cinema">Bluesound Pulse Cinema</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1176px;"><p class="vanilla-image-block" style="padding-top:56.21%;"><img id="qioXZSdaYJtwZE2zdYy4D4" name="Cinema_Lifestyle_3_1ee3d7f7-f46e-4a48-b92a-4fb1815b854a" alt="The Bluesound Pulse Cinema on an entertainment center." src="https://cdn.mos.cms.futurecdn.net/qioXZSdaYJtwZE2zdYy4D4.jpg" mos="" align="middle" fullscreen="" width="1176" height="661" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Bluesound)</span></figcaption></figure><p>If you've yet to try a BluOS product, I urge you to do so. There are plenty of multi-room audio solutions on the market, but Bluesound's is the best by far. Its latest soundbar not only uses that platform to great effect, but it delivers 3.2.2 Dolby Atmos sound via 16 drivers. It's a next-level soundbar from a company on the cutting-edge of sound performance. <em>— Nick Pino, Managing Editor</em></p><h2 id="samsung-hw-qs90h-soundbar">Samsung HW-QS90H Soundbar</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1120px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8NHTvBPSVMEhpYAuZZFtHb" name="Samsung HW-Q90H" alt="The Samsung HW-QS90H Soundbar on a black gradient background." src="https://cdn.mos.cms.futurecdn.net/8NHTvBPSVMEhpYAuZZFtHb.jpg" mos="" align="middle" fullscreen="" width="1120" height="630" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Samsung)</span></figcaption></figure><p>Want a soundbar that can seriously do it all? The Samsung QS90H is your one to watch. This all-in-one soundbar combines a powerful subwoofer with seven center channels and two surrounds — yes, in one bar. Dialogue is crystal-clear and smooth, and diegetic sounds are exceptionally detailed. It’s ideal for home setups that might not have space for a dedicated sub or rears. — <em>Erin Bashford, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="Samsung HW-QS90H"></div><h3 class="article-body__section" id="section-headphones"><span>Headphones</span></h3><h2 id="audeze-maxwell-2-anc">Audeze Maxwell 2 ANC</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4xHjavBtZVgQW3GbHynR3i" name="Audeze_Maxwell2_.JPG" alt="Audeze Maxwell 2 against a blue background" src="https://cdn.mos.cms.futurecdn.net/4xHjavBtZVgQW3GbHynR3i.jpg" 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>By adding noise cancellation to the already fantastic Maxwell 2 formula, the new <a href="https://www.tomsguide.com/gaming/gaming-peripherals/audeze-maxwell-2-review">Maxwell 2</a> is the all-rounder that Audeze’s gaming lineup has been crying out for. Its planar magnetic drivers deliver excellent fidelity, and while there are other headsets out there using similar drivers, none offer the immersion of ANC and a closed back design. — <em>Pete Wolinski, Senior Reviews Editor</em></p><div data-widget-type="merchantlink" data-model-name="Audeze Maxwell 2 ANC"></div><h2 id="belkin-soundform-mini-2-kids-wireless-headphones">Belkin SoundForm Mini 2 Kids Wireless Headphones</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1098px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="aiLPQYCSgkocT4e8wTUUJJ" name="7109uts+gUL._AC_SL1500_" alt="The Belkin SoundForm Mini 2 Kids Wireless Headphones on a child's head." src="https://cdn.mos.cms.futurecdn.net/aiLPQYCSgkocT4e8wTUUJJ.jpg" mos="" align="middle" fullscreen="" width="1098" height="618" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Belkin)</span></figcaption></figure><p>As a relatively new dad, I take my daughter’s health seriously. As desperate as I am to have her right beside me listening to music on the latest speakers and soundbars, I want to preserve her hearing health — that’s where these cheap wireless headphones come in. With a set volume cap, there’s no way for her to crank up the music to a dangerously high level, which makes me feel better about the times we do listen to music together. <em>— Nick Pino, Managing Editor</em></p><h2 id="jbl-live-780nc">JBL LIVE 780NC</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7YxFuTJ3bm4gYmtkcWDmLN" name="JBL Live 780NC 4 2.JPG" alt="the jbl live 780NC in orange photographed against the blue tom's guide background" src="https://cdn.mos.cms.futurecdn.net/7YxFuTJ3bm4gYmtkcWDmLN.jpg" 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>Bolder doesn’t always mean better, but in the case of the <a href="https://www.tomsguide.com/audio/headphones/jbl-live-780nc-review">JBL Live 780NC</a>, it does. Available in a splash of striking colors, including the bright orange we tested, the Live 780NC combine style, finesse, and good vibes. Sound quality is just as vibrant as the design, and the battery life is good enough to keep you movin’ and groovin’ all week long. — <em>Erin Bashford, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="JBL Live 780NC"></div><h2 id="meze-audio-strada">Meze Audio STRADA</h2><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="kcg5ZWzvrxaST6eNqhxi3b" name="Meze-Audio-STRADA-headphone-04" alt="The Meze Audio Strada on a black background." src="https://cdn.mos.cms.futurecdn.net/kcg5ZWzvrxaST6eNqhxi3b.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: Meze)</span></figcaption></figure><p>Audiophiles are quick to say that open-back headphones deliver a more natural, concert hall-style performance, but if you want lower bass response, you're not going to get it in open-backs. The closed-back Meze Audio STRADA delivers that clean, crisp low-end performance in spades. For high-end headphones, they're actually pretty affordable, too. <em>— Nick Pino, Managing Editor</em></p><div data-widget-type="merchantlink" data-model-name="Meze Audio Strada"></div><h3 class="article-body__section" id="section-earbuds"><span>Earbuds</span></h3><h2 id="earfun-clip-2">EarFun Clip 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Gw563LUsUGqRm9BbhHPAja" name="EarFun Clip 2" alt="EarFun Clip 2 in black on table photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/Gw563LUsUGqRm9BbhHPAja.jpg" 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 gym-goers, runners and anyone who wants to remain aware while working out, few open-earbuds are better than the <a href="https://www.tomsguide.com/audio/earbuds/earfun-clip-2-review"><u>EarFun Clip 2</u></a>. Their comfortable clip style is a winner in our books, and they’ve got AI-powered real-time translation to boot. Generally good for listening to music as well as taking calls, we were also impressed by the Clip 2’s 40-hour battery life. They’re some of the best cheap earbuds you can buy right now. — <em>Nikita Achanta, Senior Writer Reviews</em></p><h2 id="shokz-opendots-2">ShokzOpenDots 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="X3umr7xjpMtLmQUkc5LCdP" name="Shokz OpenDots 2--1-LIST" alt="Shokz OpenDots 2" src="https://cdn.mos.cms.futurecdn.net/X3umr7xjpMtLmQUkc5LCdP.jpg" mos="" align="middle" fullscreen="" width="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 <a href="https://www.tomsguide.com/audio/headphones/shokz-opendots-2-review">Shokz OpenDots 2</a> offer an impressive combination of a great fit, excellent sound quality for open buds, and long battery life. They’re ideal for runs, strength sessions and other workouts, and great for general use as well, keeping you aware of your surroundings while delivering better sound than bone conduction headphones. If you’ve yet to try clip-on headphones, the OpenDots 2 will convince you of the advantages of the design. <em>— Nick Harris-Fry, Senior Fitness Writer</em></p><div data-widget-type="merchantlink" data-model-name="Shokz OpenDots 2"></div><h2 id="technics-eah-az100-wireless-earbuds">Technics EAH-AZ100 Wireless Earbuds</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JpZWSQFwk6uZZAogPxQb2d" name="Technics EAH-AZ100 Wireless Earbuds-1-LIST" alt="Technics EAH-AZ100 Wireless Earbuds with case" src="https://cdn.mos.cms.futurecdn.net/JpZWSQFwk6uZZAogPxQb2d.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>In the pantheon of the best wireless earbuds makers, Technics holds a top spot. The Technics EAH-AZ80 that debuted in 2023 are some of my favorite earbuds of the last 10 years, and the new <a href="https://www.tomsguide.com/audio/earbuds/technics-eah-az100-wireless-earbuds-review">Technics EAH-AZ100</a> are even better. For discerning listeners looking for a pair of wireless earbuds, the Technics EAH-AZ100 deliver class-leading sound with head-tracked Dolby Atmos audio. For $249, I definitely recommend them. <em>— Nick Pino, Managing Editor </em></p><div data-widget-type="merchantlink" data-model-name="Technics EAH-AZ100 Wireless Earbuds"></div><h3 class="article-body__section" id="section-speakers"><span>Speakers</span></h3><h2 id="creative-technology-xf1">Creative Technology XF1</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:755px;"><p class="vanilla-image-block" style="padding-top:53.25%;"><img id="R4TAEmu9CQMTPGCDwkVA9k" name="1" alt="The Creative Technology XF1 desktop speakers on a desk." src="https://cdn.mos.cms.futurecdn.net/R4TAEmu9CQMTPGCDwkVA9k.jpg" mos="" align="middle" fullscreen="" width="755" height="402" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Creative)</span></figcaption></figure><p>It's not often that I get excited about computer speakers. If I'm sitting at my desk, I'm usually testing out a new pair of awesome open-back headphones or I'm going to connect wirelessly to a nearby pair of bookshelf speakers. But the Creative XF1 has me intrigued, for sure. With a 10-band parametric equalizer and more than 120 genre- and game-specific presets, it's the Swiss Army knife of desktop speakers. <em>— Nick Pino, Managing Editor</em></p><h2 id="edifier-s880db-mkii">Edifier S880DB MKII</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YRQ7UVexseHyZKTjrzepLa" name="Edifier_S880DB_MK2_ 1.JPG" alt="the edifier s880db mkii speakers in white and wood colorway showing the connectivity options, remote control, against a blue background" src="https://cdn.mos.cms.futurecdn.net/YRQ7UVexseHyZKTjrzepLa.jpg" 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>There’s a reason why the audio world made such a racket about the <a href="https://www.tomsguide.com/audio/speakers/edifier-s880db-mkii-review">Edifier S880DB MKII</a> upon release late last year. These $449 speakers look like a high-end brand and the sound matches up, too. With a rich, controlled low-end, expansive mids, and a dextrous, flexible treble, the S880DB MKII are some of the best midrange bookshelf speakers available right now. — <em>Erin Bashford, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="Edifier S880DB MKII"></div><h2 id="klipsch-the-nines-ii-powered-speakers">Klipsch The Nines II Powered Speakers</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:793px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="G9KURrBgud8Cmpqv4S94vR" name="84b4a063-8ed2-47e2-b068-5cee6b88d8d5" alt="Klipsch's The Nines II in a living room." src="https://cdn.mos.cms.futurecdn.net/G9KURrBgud8Cmpqv4S94vR.jpg" mos="" align="middle" fullscreen="" width="793" height="446" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Klipsch)</span></figcaption></figure><p>Klipsch's The Nines II were my favorite speakers of CES 2026. I heard them in a small enclosed booth on the Las Vegas Convention Center and I instantly fell in love with them. Despite their medium-sized footprint, they're bombastic. They deliver powerful, detailed sound with a fairly wide soundstage. It also comes with Dirac Live Room Correction that measures the acoustic characteristics of your room and automatically tunes the sound to prevent issues. I'm here for it. <em>— Nick Pino, Managing Editor</em></p><div data-widget-type="merchantlink" data-model-name="Klipsch The Nines II"></div><h2 id="svs-3000-micro-r-evolution-subwoofer">SVS 3000 Micro R|Evolution Subwoofer</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:867px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="8eWva2gMLdJD2wmFQDaeVJ" name="SVS 3000 Micro" alt="The SVS 3000 Micro Subwoofer on the floor in a room." src="https://cdn.mos.cms.futurecdn.net/8eWva2gMLdJD2wmFQDaeVJ.jpg" mos="" align="middle" fullscreen="" width="867" height="488" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: SVS)</span></figcaption></figure><p>Squeezing flagship subwoofer performance into a smaller package is no easy task, and yet, somehow SVS succeeded with the new 3000 Micro R|Evolution Subwoofer. Packaged in a compact 11-inch enclosure, the 3000 Micro R|Evolution Subwoofer delivers 20Hz sub-bass response using dual opposing 9-inch active drivers and a 1,200W RMS amplifier. That’s a serious sub all for under $1,000. <em>— Nick Pino, Managing Editor </em></p><h3 class="article-body__section" id="section-bluetooth-speakers"><span>Bluetooth Speakers</span></h3><h2 id="jbl-go-5">JBL Go 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:5508px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6gTtmYbVXuDQbax58Q6bqX" name="JBL-Go-5-op-ed" alt="A JBL Go 5 Bluetooth speaker in blue" src="https://cdn.mos.cms.futurecdn.net/6gTtmYbVXuDQbax58Q6bqX.jpg" mos="" align="middle" fullscreen="" width="5508" height="3098" 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>Good becomes great as JBL has introduced small but meaningful upgrades, making the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/jbl-go-5-review"><u>JBL Go 5</u></a> one of the best Bluetooth speakers of 2026. It's pocketable so you can take it anywhere with you. Even though it's small, we've been impressed by its wide soundstage and just how loud it can get. Its IP68 rating means it's highly waterproof, dustproof and drop-proof too. At just $54, it offers simply unbeatable value for money. — <em>Nikita Achanta, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="JBL Go 5"></div><h2 id="jbl-xtreme-5">JBL Xtreme 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:4956px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="MhmJUtGJcUZLnrZejP3SpU" name="Xtreme5-edit" alt="the jbl xtreme 5 with water splashing on it outside in the park showing its waterproofing" src="https://cdn.mos.cms.futurecdn.net/MhmJUtGJcUZLnrZejP3SpU.jpg" mos="" align="middle" fullscreen="" width="4956" height="2788" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Erin Bashford)</span></figcaption></figure><p>What a speaker. The <a href="https://www.tomsguide.com/audio/bluetooth-speakers/jbl-xtreme-5-review">JBL Xtreme 5</a> is officially the most durable speaker in the world thanks to its drop-proof, IP68 rating. But hardiness isn’t the only area the Xtreme 5 excels — it’s also a bass beast. Improving on its predecessor’s loud-but-muffled bass performance, the Xtreme 5 brings control, power, and ferocity to the party. The ultimate party speaker of 2026. — <em>Erin Bashford, Senior Reviews Writer</em></p><div data-widget-type="merchantlink" data-model-name="JBL Xtreme 5"></div><h2 id="sonos-play">Sonos 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:4032px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6LaUgHfNuq5PpiqN8tgajQ" name="IMG_6899.JPG" alt="Sonos Play" src="https://cdn.mos.cms.futurecdn.net/6LaUgHfNuq5PpiqN8tgajQ.jpg" mos="" align="middle" fullscreen="" width="4032" height="2268" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The <a href="https://www.tomsguide.com/audio/smart-speakers/sonos-play-review">Sonos Play</a> is a highly versatile portable speaker that flawlessly bridges the gap between everyday home use and travel listening. I recommend it for those looking to blend wireless multi-room audio capabilities with everyday Bluetooth convenience. Offering crisp vocals, satisfying bass and an impressive 24-hour battery life, this lightweight speaker delivers incredibly balanced, room-filling sound. It’s the middle ground between the bulkier <a href="https://www.tomsguide.com/reviews/sonos-move-2">Move 2</a> and smaller <a href="https://www.tomsguide.com/audio/bluetooth-speakers/sonos-roam-2-review">Roam 2</a> that Sonos users didn’t know they were waiting for. <em>— Kate Kozuch, Managing Editor</em></p><div data-widget-type="merchantlink" data-model-name="Sonos Play"></div><h3 class="article-body__section" id="section-home-hi-fi"><span>Home Hi-Fi</span></h3><h2 id="denon-avr-x2900h">Denon AVR-X2900H</h2><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="qms4cDGiCotHddjR3rm9XR" name="PDP-4" alt="The Denon AVR-X2900H on an entertainment center." src="https://cdn.mos.cms.futurecdn.net/qms4cDGiCotHddjR3rm9XR.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: Denon)</span></figcaption></figure><p>Denon knows what it’s doing when it comes to home cinema amplification. The company has been at it for decades, but blimey, this AV receiver is a bit of a belter. For a mid-range model, it’s exceptional. Movies sound convincingly cinematic, and music is rewardingly rhythmic. Thoughtful engineering, sensible connectivity and an excellent multiroom platform are the icing on the cake. <em>— Steve May, Tom’s Guide Contributor</em></p><div data-widget-type="merchantlink" data-model-name="Denon AVR-X2900H"></div><h2 id="fluance-rt87-reference-high-fidelity-dual-plinth-turntable">Fluance RT87 Reference High Fidelity Dual-Plinth Turntable</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3FaaNjzcUD4bEyXzHPi6KZ" name="Fluance RT87 turntable-1-LIST" alt="Fluance RT87 turntable on a white table" src="https://cdn.mos.cms.futurecdn.net/3FaaNjzcUD4bEyXzHPi6KZ.jpg" mos="" align="middle" fullscreen="" width="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 love the way that the RT87 looks. The dual plinth design makes it look unlike any other sub-$1000 turntable and lends a commanding presence to the deck. The carbon tonearm is a nice visual touch, and the hefty acrylic platter really brings everything together. In terms of performance, you’d be hard-pressed to find something that sounds better within $300-$500 of the RT-87’s price. <em>— Tammy Rogers, Buying Guide Editor</em></p><div data-widget-type="merchantlink" data-model-name="Fluance RT87"></div><h2 id="wiim-amp-ultra">WiiM Amp Ultra</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:942px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="NdvEjyeEynesWJHCXUtQL8" name="Wiim Amp Ultra" alt="The Wiim Amp Ultra sitting between a speaker and a turntable." src="https://cdn.mos.cms.futurecdn.net/NdvEjyeEynesWJHCXUtQL8.jpg" mos="" align="middle" fullscreen="" width="942" height="530" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Wiim)</span></figcaption></figure><p>Home entertainment centers can only hold so many products. I wish there was space for an amplifier, a streaming music player, speakers, a turntable, a pre-amp and a CD transport on every shelf. Sadly, there's not. But thankfully Wiim is there to reduce the amount of gear we need with its excellent Amp Ultra that combines a streaming player with an amplifier that can deliver up to 100W per channel. For $549, I've yet to find something better.  <em>— Nick Pino, Managing Editor</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/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><li><a href="https://www.tomsguide.com/us/best-tvs,review-2224.html">These are the 5 best TVs we've tested for movies, sports and gaming you can buy right now</a></li><li><a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">Best headphones: Top picks tested by experts</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ These $149 In-ear monitors you probably haven’t heard of give Sennheiser a run for its money — they’re a must-have for audiophiles ]]></title>
                                                                                                <dc:content><![CDATA[ <p>If you want to listen to your favorite songs the way the artist intended, you need a pair of in-ear monitors (IEMs) — and you don’t need to spend an extortionate amount on them either. The Sivga Lyrebird are phenomenal IEMs, and at just under $150, they give Sennheiser a run for its money. Sound quality is 10/10 with great instrument separation, powerful bass, and airy treble, thanks to the quad-driver configuration.</p><p>In addition to that, the Lyrebird are mighty comfortable, making them perfect for all-day wear, and they’re built well, too. The only drawback is that these IEMs utilize a 4.4mm jack, so you’ll need either an adapter or a DAC/Amp.</p><p>To find out if these are the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best wired headphones</a> for you, read my full Sivga Lyrebird review.</p><h2 class="article-body__section" id="section-sivga-lyrebird-review-specs"><span>Sivga Lyrebird 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/dp/B0H5QGSD8R/" target="_blank" rel="nofollow">$149</a> / <a href="https://sivgashop.com/products/sivga-lyrebird" target="_blank" rel="nofollow">£125</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>Quad-driver setup (10mm dynamic, balanced armature, micro-planar, and 9.2mm ceramic)</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>4.4mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>Wired</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>0.22oz (each earpiece)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Brown</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sivga-lyrebird-review-price-availability"><span>Sivga Lyrebird 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="josS8uw6u4m3PC5P2KcPEC" name="Sivga_Lyrebird_Earbuds_005.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/josS8uw6u4m3PC5P2KcPEC.jpg" 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 to stream hi-res audio, the Sivga Lyrebird are excellent in-ear monitors (IEMs) that don’t necessarily break the bank. They have a retail price of <a href="https://www.amazon.com/dp/B0H5QGSD8R/" target="_blank" rel="nofollow">$149 at Amazon U.S.</a> / <a href="https://sivgashop.com/products/sivga-lyrebird" target="_blank" rel="nofollow">£125 at Sivga Shop U.K.</a></p><p>This puts the Lyrebird in the same price range as the <a href="https://www.tomsguide.com/audio/earbuds/sennheiser-ie-200-wired-earbuds-review">Sennheiser IE 200</a> ($149) and the <a href="https://www.tomsguide.com/gaming/gaming-peripherals/sony-inzone-e9-gaming-iems-review">Sony Inzone E9</a> ($149) — though the latter are gaming IEMs. If you’re on a tight budget, the <a href="https://www.tomsguide.com/audio/earbuds/dunu-titan-x-iems-review">Dunu Titan X</a> ($39) are much cheaper than the Lyrebird, but their eartip replacement is finicky and they’re slightly heavier than the Lyrebird (more on that shortly).</p><h2 class="article-body__section" id="section-sivga-lyrebird-review-design"><span>Sivga Lyrebird 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="M4We94QtJrj4YAL5ZCWEEC" name="Sivga_Lyrebird_Earbuds_007.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/M4We94QtJrj4YAL5ZCWEEC.jpg" 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 Sivga Lyrebird are beautiful IEMs, and I was impressed with their design as soon as I unboxed them. Unlike the Sennheiser IE 200 and the Sony Inzone E9 — both of which are made of plastic — the Lyrebird are a sight for sore eyes. The Lyrebird’s earbuds feature wooden faceplates and CNC-machined aluminum housing (aviation-grade, according to Sivga) to give the IEMs a premium look.</p><p>In addition to looking great, the Lyrebird feel solid and well-built too. The Lyrebird’s braided cable features a tangle-free, flexible design, so you don’t need to worry about untangling every time you take them out of your pocket or their included case. They’re equipped with a detachable 0.78mm 2-pin cable, which means you can swap them with ease and increase the IEMs’ lifespan.</p><h2 id="weatherproofing-6">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fe5riegcStZG6zJw9mLBJC" name="Sivga_Lyrebird_Earbuds_012.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/fe5riegcStZG6zJw9mLBJC.jpg" 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>Wired earbuds and IEMs are rarely waterproof. Similar to the Dunu Titan X, the Sennheiser IE 200 and the Sony Inzone E9, the Lyrebird don’t have an official IP rating. I wouldn’t recommend wearing them in the rain or during workouts. You may want to consider a pair of the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> if you want waterproofing.</p><h2 id="comfort-8">Comfort</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LjGudezcaAQgGo8Vk87BtB" name="Sivga_Lyrebird_Earbuds_003.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/LjGudezcaAQgGo8Vk87BtB.jpg" 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>Alongside their lovely design, the Lyrebird are exceptionally comfortable, and I’ve had no problem wearing them continuously for over five hours. Each earbud weighs 0.22oz and fits snugly in my ears. The Lyrebird are lighter than the Dunu Titan X (0.45oz each) but slightly heavier than the Sennheiser IE 200 (0.14oz each) — they hit the sweet spot, in my opinion.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2964px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="Z97LeY6YzK48rqGqjbUsa7" name="Sivga_Lyrebird-2" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/Z97LeY6YzK48rqGqjbUsa7.jpg" mos="" align="middle" fullscreen="" width="2964" height="1667" 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>Just like the Dunu Titan X and the Sennheiser IE 200, you wrap the cables emerging from the earbuds around your ears to ensure they don’t fall off — and they didn’t in my testing, even when I was headbanging. I used the default size of eartips that came fitted straight out of the box, and Sivga happily includes three extra pairs of hybrid silicone tips for a stable fit and balanced seal, plus three pairs of soft silicone tips for superior comfort.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Dc592VbFyVL5PUqQna3xKC" name="Sivga_Lyrebird_Earbuds_010.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/Dc592VbFyVL5PUqQna3xKC.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also appreciate how easy it is to replace the eartips on the Lyrebird. On the Dunu Titan X, it took me 30 minutes to get a pair of eartips onto the earpieces because the tips’ opening felt ridiculously tight and rigid. On the Lyrebird, it took me about 20 seconds to replace the eartips — quick and easy.</p><h2 id="controls-13">Controls</h2><p>Similar to other IEMs I’ve tested, the Lyrebird don’t feature any in-line buttons to control playback, so you’ll need to whip out your phone or use your audio source to play and pause music.</p><h2 id="connectivity-drive">Connectivity & drive</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2pkSKw4wmhVwbR8Gy2BVLC" name="Sivga_Lyrebird_Earbuds_009.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/2pkSKw4wmhVwbR8Gy2BVLC.jpg" 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 Lyrebird are wired IEMs, so no wireless Bluetooth or 2.4GHz connectivity. That means they need to be powered (driven) by a power source. </p><p>They’re not difficult to drive, but the Lyrebird only have a 4.4mm balanced cable to connect to your audio source. The advantage of a 4.4mm jack is that it separates the audio into left and right channels to keep interference to a minimum.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6467px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kYfAYntLwwppWM5wfCpHZ8" name="Sivga_Lyrebird-3" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/kYfAYntLwwppWM5wfCpHZ8.jpg" mos="" align="middle" fullscreen="" width="6467" height="3638" attribution="" endorsement="" class="inline"></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 downside of a 4.4mm jack is that very few phones, laptops and tablets, if any, feature 4.4mm ports. Due to this, you’ll need to grab either a 4.4mm to 3.5mm adapter, or a dedicated DAC/Amp — I recommend the latter as you won’t be sacrificing the balanced channels and will be able to enjoy your tunes fully. I used the FiiO K11 R2R (<a href="https://www.amazon.com/FiiO-K11-Headphone-Amplifier-Balanced/dp/B0D9BB5RL1/" target="_blank" rel="nofollow">$175</a>) to test the Lyrebird, and can’t recommend this DAC/Amp enough if you want to get the best out of your passive headphones or IEMs.</p><p>If you want to drive straight from your phone or laptop, the Dunu Titan X and the Sennheiser IE 200 are fitted with 3.5mm jacks.</p><h2 class="article-body__section" id="section-sivga-lyrebird-review-sound-quality"><span>Sivga Lyrebird 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="c4rSLnMGCV5VBZzAednDKC" name="Sivga_Lyrebird_Earbuds_008.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/c4rSLnMGCV5VBZzAednDKC.jpg" 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>Though they’re tiny, they’re mighty — and just like the Australian bird they’re named after, the Sivga Lyrebird truly sing. They’re fitted with quad-driver setups in each earpiece. Each earpiece utilizes a combination of 10mm dynamic, 9.2mm ceramic, balanced armature, and micro-planar drivers. Sounds like overkill, I know, but each driver has its purpose.</p><p>The dynamic drivers are there for reproducing deep, elastic bass; the balanced armatures deliver clear mids and details; the micro-planar drivers enable distortion-free treble; and the ceramic drivers act as super-tweeters to handle ultra-high frequencies. All of this is to say that the Lyrebird make music sound phenomenal. As I mentioned earlier, I tested the Lyrebird by plugging them into the FiiO K11, and listening to my mix of testing songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for hi-res audio.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gwfLeqMFEedBk3purvioKC" name="Sivga_Lyrebird_Earbuds_004.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/gwfLeqMFEedBk3purvioKC.jpg" 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 instrument separation and different atmospheric layers in Pink Floyd’s ‘<a href="https://open.qobuz.com/track/47683554" target="_blank" rel="nofollow">Time</a>’ jumped out as soon as I played the song. This is my favorite song of all time, and listening to it through the Lyrebird is the quintessential way. From the chiming clocks to the layered guitars and subtle keyboard textures, every element occupied its own distinct space while maintaining a cohesive melody. The track sounded and felt expansive and three-dimensional, while the vocals remained focused at the center.</p><p>Speaking of, ‘<a href="https://open.qobuz.com/track/19512579" target="_blank" rel="nofollow">The Chain</a>’ by Fleetwood Mac felt like an excellent showcase for the Lyrebird’s vocal presentation. The IEMs allowed each voice to remain distinct while blending seamlessly together. Lead vocals sounded expressive and packed plenty of detail, while the treble felt crisp and vibrant. Cymbals and acoustic guitar overtones had plenty of sparkle to them without coming across as fatiguing. I also appreciate that the sibilance remained smooth — “s” and “t” sounds weren’t harsh and didn’t sound like they were bleeding into one another.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="euwewcPxfPZc4GCpyosbHC" name="Sivga_Lyrebird_Earbuds_013.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/euwewcPxfPZc4GCpyosbHC.jpg" 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>Dire Straits’ ‘<a href="https://open.qobuz.com/track/161150239" target="_blank" rel="nofollow">Sultans of Swing</a>’ highlighted the Lyrebird’s ability to balance precision and musicality. The bassline sounded tight and articulate with plenty of <em>oomph </em>to it, and it provided a solid rhythmic foundation without overshadowing the rest of the mix. Every note plucked and the slides on Mark Knopfler’s guitar came through with exceptional clarity. The track sounded the most energetic it ever has, and I love that the Lyrebird successfully maintain the classic’s dynamic, engaging, and groovy energy.</p><p>I’ve also been impressed by the Lyrebird’s ability to remain distortion-free even when listening to music at high volumes (you shouldn’t be listening to music at 100% volume though as it can potentially damage your ears). Charli XCX’s electronic track ‘<a href="https://open.qobuz.com/track/270514776" target="_blank" rel="nofollow">Von Dutch</a>’ never sounded distorted at full volume, as the pounding kickdrums hit with authority and the bass remained tight and never bloated.</p><p><em>Listen along to my review with the Sivga Lyrebird testing playlist:</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/68316691?zone=GB-en"></iframe><h2 class="article-body__section" id="section-sivga-lyrebird-review-verdict"><span>Sivga Lyrebird 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="wfopDcsuBMur2yDaAthtAC" name="Sivga_Lyrebird_Earbuds_002.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/wfopDcsuBMur2yDaAthtAC.jpg" 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 Sivga Lyrebird are simply outstanding. That’s it. That’s all you need to know; my job here is done.</p><p>No but really: the Lyrebird make music sound extremely detailed and like you’re in the recording studio with your favorite artist. With excellent instrument separation, deep and punchy bass, vibrant treble, and refined mids, the Lyrebird don’t give you lots to complain about.</p><p>They’re also extremely comfortable for all-day wear, and not only are they built well, they’re pleasing to look at, too. All of these qualities make the Lyrebird easy to recommend, especially if you need a pair of IEMs that doesn’t break the bank. Because these IEMs utilize a 4.4mm jack, you’ll need to get a DAC or an adapter — but that’s about the only drawback I can think of.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/sivga-lyrebird-iems-review</link>
                                                                            <description>
                            <![CDATA[ The Sivga Lyrebird are comfortable IEMs with a quad-driver setup, and they boast incredible instrument separation, powerful bass, and clean treble. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">odtAKbbov3pm2nouxpau6d</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/pWqwHjJozv8YYPjqeHT9y6-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 09 Aug 2026 11:30: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.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/pWqwHjJozv8YYPjqeHT9y6-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Sivga Lyrebird IEMs]]></media:description>                                                            <media:text><![CDATA[Sivga Lyrebird IEMs]]></media:text>
                                <media:title type="plain"><![CDATA[Sivga Lyrebird IEMs]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/pWqwHjJozv8YYPjqeHT9y6-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you want to listen to your favorite songs the way the artist intended, you need a pair of in-ear monitors (IEMs) — and you don’t need to spend an extortionate amount on them either. The Sivga Lyrebird are phenomenal IEMs, and at just under $150, they give Sennheiser a run for its money. Sound quality is 10/10 with great instrument separation, powerful bass, and airy treble, thanks to the quad-driver configuration.</p><p>In addition to that, the Lyrebird are mighty comfortable, making them perfect for all-day wear, and they’re built well, too. The only drawback is that these IEMs utilize a 4.4mm jack, so you’ll need either an adapter or a DAC/Amp.</p><p>To find out if these are the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best wired headphones</a> for you, read my full Sivga Lyrebird review.</p><h2 class="article-body__section" id="section-sivga-lyrebird-review-specs"><span>Sivga Lyrebird 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/dp/B0H5QGSD8R/" target="_blank" rel="nofollow">$149</a> / <a href="https://sivgashop.com/products/sivga-lyrebird" target="_blank" rel="nofollow">£125</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>Quad-driver setup (10mm dynamic, balanced armature, micro-planar, and 9.2mm ceramic)</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz-20KHz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>4.4mm wired</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>ANC</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>Wired</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>0.22oz (each earpiece)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Brown</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sivga-lyrebird-review-price-availability"><span>Sivga Lyrebird 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="josS8uw6u4m3PC5P2KcPEC" name="Sivga_Lyrebird_Earbuds_005.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/josS8uw6u4m3PC5P2KcPEC.jpg" 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 to stream hi-res audio, the Sivga Lyrebird are excellent in-ear monitors (IEMs) that don’t necessarily break the bank. They have a retail price of <a href="https://www.amazon.com/dp/B0H5QGSD8R/" target="_blank" rel="nofollow">$149 at Amazon U.S.</a> / <a href="https://sivgashop.com/products/sivga-lyrebird" target="_blank" rel="nofollow">£125 at Sivga Shop U.K.</a></p><p>This puts the Lyrebird in the same price range as the <a href="https://www.tomsguide.com/audio/earbuds/sennheiser-ie-200-wired-earbuds-review">Sennheiser IE 200</a> ($149) and the <a href="https://www.tomsguide.com/gaming/gaming-peripherals/sony-inzone-e9-gaming-iems-review">Sony Inzone E9</a> ($149) — though the latter are gaming IEMs. If you’re on a tight budget, the <a href="https://www.tomsguide.com/audio/earbuds/dunu-titan-x-iems-review">Dunu Titan X</a> ($39) are much cheaper than the Lyrebird, but their eartip replacement is finicky and they’re slightly heavier than the Lyrebird (more on that shortly).</p><h2 class="article-body__section" id="section-sivga-lyrebird-review-design"><span>Sivga Lyrebird 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="M4We94QtJrj4YAL5ZCWEEC" name="Sivga_Lyrebird_Earbuds_007.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/M4We94QtJrj4YAL5ZCWEEC.jpg" 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 Sivga Lyrebird are beautiful IEMs, and I was impressed with their design as soon as I unboxed them. Unlike the Sennheiser IE 200 and the Sony Inzone E9 — both of which are made of plastic — the Lyrebird are a sight for sore eyes. The Lyrebird’s earbuds feature wooden faceplates and CNC-machined aluminum housing (aviation-grade, according to Sivga) to give the IEMs a premium look.</p><p>In addition to looking great, the Lyrebird feel solid and well-built too. The Lyrebird’s braided cable features a tangle-free, flexible design, so you don’t need to worry about untangling every time you take them out of your pocket or their included case. They’re equipped with a detachable 0.78mm 2-pin cable, which means you can swap them with ease and increase the IEMs’ lifespan.</p><h2 id="weatherproofing-6">Weatherproofing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fe5riegcStZG6zJw9mLBJC" name="Sivga_Lyrebird_Earbuds_012.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/fe5riegcStZG6zJw9mLBJC.jpg" 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>Wired earbuds and IEMs are rarely waterproof. Similar to the Dunu Titan X, the Sennheiser IE 200 and the Sony Inzone E9, the Lyrebird don’t have an official IP rating. I wouldn’t recommend wearing them in the rain or during workouts. You may want to consider a pair of the <a href="https://www.tomsguide.com/us/best-wireless-earbuds,review-6160.html">best wireless earbuds</a> if you want waterproofing.</p><h2 id="comfort-8">Comfort</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LjGudezcaAQgGo8Vk87BtB" name="Sivga_Lyrebird_Earbuds_003.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/LjGudezcaAQgGo8Vk87BtB.jpg" 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>Alongside their lovely design, the Lyrebird are exceptionally comfortable, and I’ve had no problem wearing them continuously for over five hours. Each earbud weighs 0.22oz and fits snugly in my ears. The Lyrebird are lighter than the Dunu Titan X (0.45oz each) but slightly heavier than the Sennheiser IE 200 (0.14oz each) — they hit the sweet spot, in my opinion.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2964px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="Z97LeY6YzK48rqGqjbUsa7" name="Sivga_Lyrebird-2" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/Z97LeY6YzK48rqGqjbUsa7.jpg" mos="" align="middle" fullscreen="" width="2964" height="1667" 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>Just like the Dunu Titan X and the Sennheiser IE 200, you wrap the cables emerging from the earbuds around your ears to ensure they don’t fall off — and they didn’t in my testing, even when I was headbanging. I used the default size of eartips that came fitted straight out of the box, and Sivga happily includes three extra pairs of hybrid silicone tips for a stable fit and balanced seal, plus three pairs of soft silicone tips for superior comfort.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Dc592VbFyVL5PUqQna3xKC" name="Sivga_Lyrebird_Earbuds_010.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/Dc592VbFyVL5PUqQna3xKC.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I also appreciate how easy it is to replace the eartips on the Lyrebird. On the Dunu Titan X, it took me 30 minutes to get a pair of eartips onto the earpieces because the tips’ opening felt ridiculously tight and rigid. On the Lyrebird, it took me about 20 seconds to replace the eartips — quick and easy.</p><h2 id="controls-13">Controls</h2><p>Similar to other IEMs I’ve tested, the Lyrebird don’t feature any in-line buttons to control playback, so you’ll need to whip out your phone or use your audio source to play and pause music.</p><h2 id="connectivity-drive">Connectivity & drive</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2pkSKw4wmhVwbR8Gy2BVLC" name="Sivga_Lyrebird_Earbuds_009.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/2pkSKw4wmhVwbR8Gy2BVLC.jpg" 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 Lyrebird are wired IEMs, so no wireless Bluetooth or 2.4GHz connectivity. That means they need to be powered (driven) by a power source. </p><p>They’re not difficult to drive, but the Lyrebird only have a 4.4mm balanced cable to connect to your audio source. The advantage of a 4.4mm jack is that it separates the audio into left and right channels to keep interference to a minimum.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6467px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kYfAYntLwwppWM5wfCpHZ8" name="Sivga_Lyrebird-3" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/kYfAYntLwwppWM5wfCpHZ8.jpg" mos="" align="middle" fullscreen="" width="6467" height="3638" attribution="" endorsement="" class="inline"></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 downside of a 4.4mm jack is that very few phones, laptops and tablets, if any, feature 4.4mm ports. Due to this, you’ll need to grab either a 4.4mm to 3.5mm adapter, or a dedicated DAC/Amp — I recommend the latter as you won’t be sacrificing the balanced channels and will be able to enjoy your tunes fully. I used the FiiO K11 R2R (<a href="https://www.amazon.com/FiiO-K11-Headphone-Amplifier-Balanced/dp/B0D9BB5RL1/" target="_blank" rel="nofollow">$175</a>) to test the Lyrebird, and can’t recommend this DAC/Amp enough if you want to get the best out of your passive headphones or IEMs.</p><p>If you want to drive straight from your phone or laptop, the Dunu Titan X and the Sennheiser IE 200 are fitted with 3.5mm jacks.</p><h2 class="article-body__section" id="section-sivga-lyrebird-review-sound-quality"><span>Sivga Lyrebird 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="c4rSLnMGCV5VBZzAednDKC" name="Sivga_Lyrebird_Earbuds_008.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/c4rSLnMGCV5VBZzAednDKC.jpg" 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>Though they’re tiny, they’re mighty — and just like the Australian bird they’re named after, the Sivga Lyrebird truly sing. They’re fitted with quad-driver setups in each earpiece. Each earpiece utilizes a combination of 10mm dynamic, 9.2mm ceramic, balanced armature, and micro-planar drivers. Sounds like overkill, I know, but each driver has its purpose.</p><p>The dynamic drivers are there for reproducing deep, elastic bass; the balanced armatures deliver clear mids and details; the micro-planar drivers enable distortion-free treble; and the ceramic drivers act as super-tweeters to handle ultra-high frequencies. All of this is to say that the Lyrebird make music sound phenomenal. As I mentioned earlier, I tested the Lyrebird by plugging them into the FiiO K11, and listening to my mix of testing songs on <a href="https://www.tomsguide.com/audio/qobuz-review">Qobuz</a>, the <a href="https://www.tomsguide.com/buying-guide/best-music-streaming-services">best music streaming service</a> for hi-res audio.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gwfLeqMFEedBk3purvioKC" name="Sivga_Lyrebird_Earbuds_004.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/gwfLeqMFEedBk3purvioKC.jpg" 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 instrument separation and different atmospheric layers in Pink Floyd’s ‘<a href="https://open.qobuz.com/track/47683554" target="_blank" rel="nofollow">Time</a>’ jumped out as soon as I played the song. This is my favorite song of all time, and listening to it through the Lyrebird is the quintessential way. From the chiming clocks to the layered guitars and subtle keyboard textures, every element occupied its own distinct space while maintaining a cohesive melody. The track sounded and felt expansive and three-dimensional, while the vocals remained focused at the center.</p><p>Speaking of, ‘<a href="https://open.qobuz.com/track/19512579" target="_blank" rel="nofollow">The Chain</a>’ by Fleetwood Mac felt like an excellent showcase for the Lyrebird’s vocal presentation. The IEMs allowed each voice to remain distinct while blending seamlessly together. Lead vocals sounded expressive and packed plenty of detail, while the treble felt crisp and vibrant. Cymbals and acoustic guitar overtones had plenty of sparkle to them without coming across as fatiguing. I also appreciate that the sibilance remained smooth — “s” and “t” sounds weren’t harsh and didn’t sound like they were bleeding into one another.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="euwewcPxfPZc4GCpyosbHC" name="Sivga_Lyrebird_Earbuds_013.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/euwewcPxfPZc4GCpyosbHC.jpg" 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>Dire Straits’ ‘<a href="https://open.qobuz.com/track/161150239" target="_blank" rel="nofollow">Sultans of Swing</a>’ highlighted the Lyrebird’s ability to balance precision and musicality. The bassline sounded tight and articulate with plenty of <em>oomph </em>to it, and it provided a solid rhythmic foundation without overshadowing the rest of the mix. Every note plucked and the slides on Mark Knopfler’s guitar came through with exceptional clarity. The track sounded the most energetic it ever has, and I love that the Lyrebird successfully maintain the classic’s dynamic, engaging, and groovy energy.</p><p>I’ve also been impressed by the Lyrebird’s ability to remain distortion-free even when listening to music at high volumes (you shouldn’t be listening to music at 100% volume though as it can potentially damage your ears). Charli XCX’s electronic track ‘<a href="https://open.qobuz.com/track/270514776" target="_blank" rel="nofollow">Von Dutch</a>’ never sounded distorted at full volume, as the pounding kickdrums hit with authority and the bass remained tight and never bloated.</p><p><em>Listen along to my review with the Sivga Lyrebird testing playlist:</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/68316691?zone=GB-en"></iframe><h2 class="article-body__section" id="section-sivga-lyrebird-review-verdict"><span>Sivga Lyrebird 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="wfopDcsuBMur2yDaAthtAC" name="Sivga_Lyrebird_Earbuds_002.JPG" alt="Sivga Lyrebird IEMs" src="https://cdn.mos.cms.futurecdn.net/wfopDcsuBMur2yDaAthtAC.jpg" 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 Sivga Lyrebird are simply outstanding. That’s it. That’s all you need to know; my job here is done.</p><p>No but really: the Lyrebird make music sound extremely detailed and like you’re in the recording studio with your favorite artist. With excellent instrument separation, deep and punchy bass, vibrant treble, and refined mids, the Lyrebird don’t give you lots to complain about.</p><p>They’re also extremely comfortable for all-day wear, and not only are they built well, they’re pleasing to look at, too. All of these qualities make the Lyrebird easy to recommend, especially if you need a pair of IEMs that doesn’t break the bank. Because these IEMs utilize a 4.4mm jack, you’ll need to get a DAC or an adapter — but that’s about the only drawback I can think of.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ 19 best headphone, earbud and speaker deals on Amazon — save up to 50% on Bose, Apple, JBL and more ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Summer is the perfect time to upgrade your audio setup with the high-quality headphones, earbuds or speakers you've had your eye on. Even better, Amazon is offering up to 50% off top-rated audio gear, making now a great time to save.</p><p>Amazon's sale has deals for every budget. Some of the <a href="https://www.tomsguide.com/us/best-headphones-deals,news-28645.html">best headphones </a>we've tested from brands like JBL, Bose, Apple, and Sony are discounted to as little as $38. You'll also find great savings on some of our favorite speakers, including <a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow">the Bose SoundLink Micro for just $99</a>. </p><p>If you're looking to upgrade your audio devices for summer travel, pool parties, beach days and more, keep scrolling. I've rounded up all my favorite audio device deals on Amazon that will get you the most bang for your buck! </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links </span></h3><ul><li><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>shop all deals on Amazon</strong></a></li><li><strong>Sony WH-CH520: </strong><a href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow"><strong>was $59 now $38</strong></a></li><li><strong>Beats Flex Wireless: </strong><a href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow"><strong>was $69 now $45</strong></a></li><li><strong>Apple AirPods 4: </strong><a href="https://www.amazon.com/dp/B0DGHMNQ5Z/" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Bose SoundLink Micro: </strong><a href="https://www.amazon.com/dp/B0FDLD34V5" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Beats Studio Pro: </strong><a href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" target="_blank" rel="nofollow"><strong>was $349 now $169</strong></a></li></ul><h3 class="article-body__section" id="section-bose-deals"><span>Bose Deals</span></h3><div class="product"><a data-dimension112="72d40a54-90db-11f1-8b44-b15be0f8e0c6" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1105px;"><p class="vanilla-image-block" style="padding-top:110.05%;"><img id="4S8VXWvZTpFMLYd8oiNBC7" name="Bose Micro" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4S8VXWvZTpFMLYd8oiNBC7.jpg" mos="" align="middle" fullscreen="" width="1105" height="1216" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow" data-dimension112="72d40a54-90db-11f1-8b44-b15be0f8e0c6" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99">SoundLink Micro Portable Bluetooth Speaker: was $129 now $99</a></strong><br>The SoundLink Micro is a compact, portable speaker you can take on the road. The Bluetooth speaker is waterproof (IPX7 certified) and offers up to 6 hours of play time off a single charge. It can also be paired with another SoundLink speaker to create a stereo mode.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow" data-dimension112="72d40a54-90db-11f1-8b44-b15be0f8e0c6" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="72d40acc-90db-11f1-91e8-f935d50bbc46" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:75.11%;"><img id="fexRzMYRvYZTHGfbcUbFf8" name="Bose speaker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/fexRzMYRvYZTHGfbcUbFf8.jpg" mos="" align="middle" fullscreen="" width="679" height="510" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow" data-dimension112="72d40acc-90db-11f1-91e8-f935d50bbc46" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119">SoundLink Flex (Gen 2): was $149 now $119</a></strong><br>The new Bose SoundLink (2nd gen) is a compact and powerful Bluetooth speaker with a durable design. This 1.2-pound speaker is water and dust proof and delivered great sound quality in our <a href="https://www.tomsguide.com/audio/bose-soundlink-flex-2-review" target="_blank">SoundLink Flex gen 2 review</a>. With 12 hours of battery life and $30 off, you have yourself a great deal.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow" data-dimension112="72d40acc-90db-11f1-91e8-f935d50bbc46" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119">View Deal</a></p></div><div class="product"><a data-dimension112="72d40b3a-90db-11f1-80c2-6551ca6b366f" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1268px;"><p class="vanilla-image-block" style="padding-top:62.38%;"><img id="qDBpkjESq4Z3UsvHc4vEeX" name="bose open earbuds" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qDBpkjESq4Z3UsvHc4vEeX.jpg" mos="" align="middle" fullscreen="" width="1268" height="791" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="72d40b3a-90db-11f1-80c2-6551ca6b366f" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">Ultra Open Earbuds: was $299 now $199</a></strong><br>The Bose Ultra Open perfected the open earbud formula. They have a comfortable, non-invasive fit, solid battery life, and they sound better than you might expect from open buds. They got <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds">4 stars in our review</a>, where we loved their simple controls and surprising lack of sound leakage. <a class="view-deal button" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="72d40b3a-90db-11f1-80c2-6551ca6b366f" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="72d40ba8-90db-11f1-8d93-678f9f1d8429" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$309" href="https://www.amazon.com/dp/B0CVL1K7DX/" 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="dT2cC2J8saDETsZvrBwfqk" name="Bose_SoundLink_Max_square.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/dT2cC2J8saDETsZvrBwfqk.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>With big sound and a fashionable look, the SoundLink Max is a great portable companion for taking on the go. 20 hours of battery life is a big bonus as well, giving you plenty of listening time. <a class="view-deal button" href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow" data-dimension112="72d40ba8-90db-11f1-8d93-678f9f1d8429" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$309">View Deal</a></p></div><div class="product"><a data-dimension112="72d40c16-90db-11f1-ac27-89f31c84db62" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$339" href="https://www.amazon.com/dp/B0CCZ1L489?" 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="g64SDV3bmD2tcgopiiNgP6" name="Bose QuietComfort Ultra headphones Versus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/g64SDV3bmD2tcgopiiNgP6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Headphones get you epic ANC, super sound, and the most comfortable fit this side of wrapping your head in a snood that plays music. 24 hours of battery life isn't the best, but they make up for it with just about everything else.<a class="view-deal button" href="https://www.amazon.com/dp/B0CCZ1L489?" target="_blank" rel="nofollow" data-dimension112="72d40c16-90db-11f1-ac27-89f31c84db62" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$339">View Deal</a></p></div><h3 class="article-body__section" id="section-apple-deals"><span>Apple Deals</span></h3><div class="product"><a data-dimension112="72d40cfc-90db-11f1-a077-43910f515ad0" data-action="Deal Block" data-label="AirPods 4" data-dimension48="AirPods 4" data-dimension25="$99" href="https://www.amazon.com/dp/B0DGHMNQ5Z/" 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="kTKCG8gCBjg4vScr6CMgoN" name="AirPods 4 BG image.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kTKCG8gCBjg4vScr6CMgoN.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods 4 are about as barebones as AirPods come, but they're still packed with features. Spatial Audio is on board for example, something you'd have to pay more for on the old model. 5 hours of battery isn't the very best, but they're a great on boarding place for the AirPods line.<a class="view-deal button" href="https://www.amazon.com/dp/B0DGHMNQ5Z/" target="_blank" rel="nofollow" data-dimension112="72d40cfc-90db-11f1-a077-43910f515ad0" data-action="Deal Block" data-label="AirPods 4" data-dimension48="AirPods 4" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="72d40da6-90db-11f1-8c6e-cb92d8ce214a" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$199" href="https://www.amazon.com/Apple-Cancellation-Translation-Headphones-High-Fidelity/dp/B0FQFB8FMG" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:842px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="FYzcjQSdtuwidVacXmMNL5" name="AirPods Pro 3.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FYzcjQSdtuwidVacXmMNL5.jpg" mos="" align="middle" fullscreen="" width="842" height="842" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods Pro 3 bring the heat with 10 hours of battery life, improved sound, and superior active noise canceling. In our <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review" data-dimension112="72d40da6-90db-11f1-8c6e-cb92d8ce214a" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$199">AirPods Pro 3 review</a> we said the Editor's Choice buds deliver the upgrade iPhone users have been waiting for with improved battery life, stronger noise cancellation, and smart new features like Live Translation, which delivers real-time translations to your ears. <a class="view-deal button" href="https://www.amazon.com/Apple-Cancellation-Translation-Headphones-High-Fidelity/dp/B0FQFB8FMG" target="_blank" rel="nofollow" data-dimension112="72d40da6-90db-11f1-8c6e-cb92d8ce214a" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="72d40e1e-90db-11f1-898d-ef286b0e1a5e" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$449" href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS72GZJ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1356px;"><p class="vanilla-image-block" style="padding-top:110.62%;"><img id="augarq3rWrbuXENFrmUU88" name="airpods max 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/augarq3rWrbuXENFrmUU88.jpg" mos="" align="middle" fullscreen="" width="1356" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods Max 2 are Apple's latest flagship over-ears, with hi-res audio playback via USB-C and Bluetooth, seamless Siri integration and smart features like Live Translation, intuitive controls, and a sleek, iconic design.<a class="view-deal button" href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS72GZJ/" target="_blank" rel="nofollow" data-dimension112="72d40e1e-90db-11f1-898d-ef286b0e1a5e" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$449">View Deal</a></p></div><h3 class="article-body__section" id="section-beats-deals"><span>Beats Deals</span></h3><div class="product"><a data-dimension112="72d40f22-90db-11f1-9904-73872852650a" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45" href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:103.68%;"><img id="FGXTiRGeZrNnRV5M5SvbR9" name="Beats Flex" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FGXTiRGeZrNnRV5M5SvbR9.jpg" mos="" align="middle" fullscreen="" width="679" height="704" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow" data-dimension112="72d40f22-90db-11f1-9904-73872852650a" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45">Flex Wireless: was $69 now $45</a></strong><br>While they aren't <em>technically </em>true wireless, on account of the wire that connects the buds, the Flex are a great pair of fitness earbuds. They're super comfortable thanks to their lightweight, and this $24 saving is a solid discount.<a class="view-deal button" href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow" data-dimension112="72d40f22-90db-11f1-9904-73872852650a" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45">View Deal</a></p></div><div class="product"><a data-dimension112="72d40fcc-90db-11f1-a2e6-c960b6c6c5f9" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99" href="https://www.amazon.com/dp/B0C2W1KLSS/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:107.51%;"><img id="Nu3NHDhGaSDd5XGizimnyT" name="Studio Buds plus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Nu3NHDhGaSDd5XGizimnyT.jpg" mos="" align="middle" fullscreen="" width="679" height="730" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Studio Buds are a wonderful little pair of buds, with some big improvements over the last model. 9 hours of battery life is exemplary, extended to 36 hours with the charging case, and they're some of the most comfortable wireless in-ears around. In our <a href="https://www.tomsguide.com/reviews/beats-studio-buds-plus" data-dimension112="72d40fcc-90db-11f1-a2e6-c960b6c6c5f9" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99">Beats Studio Buds Plus review</a>, we gave them 4 stars thanks to their great design and sound.<a class="view-deal button" href="https://www.amazon.com/dp/B0C2W1KLSS/" target="_blank" rel="nofollow" data-dimension112="72d40fcc-90db-11f1-a2e6-c960b6c6c5f9" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="72d41076-90db-11f1-a9f9-730c5d8266e9" data-action="Deal Block" data-label="Pill" data-dimension48="Pill" data-dimension25="$99" href="https://www.amazon.com/gp/aw/d/B0D4STT6C2/" 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="DG5WgcSHFZLZ9WcXutJuLT" name="Beats Pill" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DG5WgcSHFZLZ9WcXutJuLT.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In our <a href="https://www.tomsguide.com/audio/beats-pill-2024-tested-5-things-i-like-3-that-i-dont" data-dimension112="72d41076-90db-11f1-a9f9-730c5d8266e9" data-action="Deal Block" data-label="Pill" data-dimension48="Pill" data-dimension25="$99">Beats Pill hands-on review</a>, we found a speaker that has (as you might expect) some massive bass and a slick look. Battery life is respectable at 24 hours, and its waterproofing makes it perfect for pool parties and outdoor use.<a class="view-deal button" href="https://www.amazon.com/gp/aw/d/B0D4STT6C2/" target="_blank" rel="nofollow" data-dimension112="72d41076-90db-11f1-a9f9-730c5d8266e9" data-action="Deal Block" data-label="Pill" data-dimension48="Pill" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="72d41134-90db-11f1-af04-efa2390bbb34" data-action="Deal Block" data-label="Solo 4" data-dimension48="Solo 4" data-dimension25="$129" href="https://www.amazon.com/dp/B0CZPHPJLN/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:717px;"><p class="vanilla-image-block" style="padding-top:154.25%;"><img id="ZDYGygzUQQsApUVGtH5RBe" name="510fGxoTsTL._AC_SL1500_.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ZDYGygzUQQsApUVGtH5RBe.jpg" mos="" align="middle" fullscreen="" width="717" height="1106" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Solo 4 might not feature noise canceling, but they make up for it by sounding pretty good. They're comfortable as well thanks to Beats new foam, and they fold up super small so that they can fit into any bag. There's also loads of battery life — 50 hours, to be exact.<a class="view-deal button" href="https://www.amazon.com/dp/B0CZPHPJLN/" target="_blank" rel="nofollow" data-dimension112="72d41134-90db-11f1-af04-efa2390bbb34" data-action="Deal Block" data-label="Solo 4" data-dimension48="Solo 4" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="72d41198-90db-11f1-9cb7-0dd17c54575f" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169" href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" 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="WbdEkaq3pnr5qszwU9U9aX" name="Beats Studio Pro on white.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WbdEkaq3pnr5qszwU9U9aX.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Studio Pro are specifically designed to bring back that thumping bass Beats is known for. They offer improved active noise cancelation and boosted battery life (up to 40 hours with ANC turned off). There's also Spatial Audio support and Beats' signature strong brand look. However, our <a href="https://www.tomsguide.com/reviews/beats-studio-pro-review" data-dimension112="72d41198-90db-11f1-9cb7-0dd17c54575f" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169">Beats Studio Pro review</a> found the clamping force high, which may affect comfort levels for some wearers. Originally priced at $349, they've now on sale for over half off.<a class="view-deal button" href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" target="_blank" rel="nofollow" data-dimension112="72d41198-90db-11f1-9cb7-0dd17c54575f" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169">View Deal</a></p></div><h3 class="article-body__section" id="section-sony-deals"><span>Sony Deals</span></h3><div class="product"><a data-dimension112="72d41256-90db-11f1-b14d-178f6e757603" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38" href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:510px;"><p class="vanilla-image-block" style="padding-top:154.90%;"><img id="bzH89XJXSHScmHcf8rgiyP" name="Sony_WH-C520_Headphones_cappucino_deal_block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bzH89XJXSHScmHcf8rgiyP.jpg" mos="" align="middle" fullscreen="" width="510" height="790" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Our <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-ch520-review" data-dimension112="72d41256-90db-11f1-b14d-178f6e757603" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38">Sony WH-CH520 headphones review</a> said these headphones "deliver on battery life and performance."  They're 360 Reality Audio compatible for more 3D sound and you can customize EQ in the Sony Headphones app. Although there's no active noise cancelling function, they have built-in mics for hands-free voice and video calls and noise suppression processing to keep your voice sounding clear in noisy environments. They're also rated for up to 50 hours of battery life.<a class="view-deal button" href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow" data-dimension112="72d41256-90db-11f1-b14d-178f6e757603" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38">View Deal</a></p></div><div class="product"><a data-dimension112="72d412e2-90db-11f1-9d1c-d92b5bdbddac" data-action="Deal Block" data-label="SRS-XB100" data-dimension48="SRS-XB100" data-dimension25="$53" href="https://www.amazon.com/Sony-Lightweight-Super-Compact-Extra-Durable-Waterproof/dp/B0C296VD7T?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1428px;"><p class="vanilla-image-block" style="padding-top:105.04%;"><img id="2HoERCRCD9zg7NesyDvkZC" name="sony-srs-xb13-speaker.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/2HoERCRCD9zg7NesyDvkZC.jpg" mos="" align="middle" fullscreen="" width="1428" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Sony-Lightweight-Super-Compact-Extra-Durable-Waterproof/dp/B0C296VD7T?" target="_blank" rel="nofollow" data-dimension112="72d412e2-90db-11f1-9d1c-d92b5bdbddac" data-action="Deal Block" data-label="SRS-XB100" data-dimension48="SRS-XB100" data-dimension25="$53">SRS-XB100: was $59 now $53</a></strong><br>One of the best Bluetooth speakers in terms of portability is on sale right now at Amazon. Believe it or not, this 0.6-pound speaker packs a punch. The orange, light grey and black colors are all included in the promotion. <a class="view-deal button" href="https://www.amazon.com/Sony-Lightweight-Super-Compact-Extra-Durable-Waterproof/dp/B0C296VD7T?" target="_blank" rel="nofollow" data-dimension112="72d412e2-90db-11f1-9d1c-d92b5bdbddac" data-action="Deal Block" data-label="SRS-XB100" data-dimension48="SRS-XB100" data-dimension25="$53">View Deal</a></p></div><div class="product"><a data-dimension112="72d41418-90db-11f1-8e6d-f3f948646660" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248" href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1509px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="zpDUbSJksxwfgT3KDaf4WS" name="Sony WF-1000XM5.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zpDUbSJksxwfgT3KDaf4WS.jpg" mos="" align="middle" fullscreen="" width="1509" height="1509" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow" data-dimension112="72d41418-90db-11f1-8e6d-f3f948646660" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248">WF-1000XM5: was $329 now $248</a></strong><br>These are some of the best buds that you can buy right now. They sound excellent, with some wickedly detailed sound with plenty of low-end grunt, and feature a very comfortable fit. We gave them 4.5 stars in <a href="https://www.tomsguide.com/reviews/sony-wf-1000xm5">our review</a> because of their great ANC, which manages to block out almost any noise you could throw at them. This isn't <em>quite</em> their lowest price ever, but it's very close.<a class="view-deal button" href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow" data-dimension112="72d41418-90db-11f1-8e6d-f3f948646660" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248">View Deal</a></p></div><div class="product"><a data-dimension112="72d4147c-90db-11f1-a163-fbd2d05412b6" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248" href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B0BXYCS74H?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1381px;"><p class="vanilla-image-block" style="padding-top:108.62%;"><img id="TWLQcjD9oitTFCdEHsgTe5" name="Sony WH-1000XM5 deal block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TWLQcjD9oitTFCdEHsgTe5.jpg" mos="" align="middle" fullscreen="" width="1381" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B0BXYCS74H?" target="_blank" rel="nofollow" data-dimension112="72d4147c-90db-11f1-a163-fbd2d05412b6" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248">WH-1000XM5: was $399 now $248</a></strong><br>The XM5s are Sony's premium noise-canceling headphones and our pick for the best headphones on the market. The large, over-ear cushions are incredibly comfortable and the active noise cancelation is excellent. Plus, despite ANC being a drain on battery life, these amazing headphones still give you up to 30 hours with ANC enabled. You can push that up to 40 hours of battery life with ANC turned off.<a class="view-deal button" href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B0BXYCS74H?" target="_blank" rel="nofollow" data-dimension112="72d4147c-90db-11f1-a163-fbd2d05412b6" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248">View Deal</a></p></div><h3 class="article-body__section" id="section-jbl-deals"><span>JBL Deals</span></h3><div class="product"><a data-dimension112="72d41544-90db-11f1-9a21-cb8caaa7cb47" data-action="Deal Block" data-label="Go 4 Bluetooth Speaker" data-dimension48="Go 4 Bluetooth Speaker" data-dimension25="$39" href="https://www.amazon.com/JBL-Ultra-Portable-Waterproof-Dustproof-Built/dp/B0CTNWKGM2" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:105.30%;"><img id="PfFcSd7yE4rJP5xxATqJ3e" name="JBL Go" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/PfFcSd7yE4rJP5xxATqJ3e.jpg" mos="" align="middle" fullscreen="" width="679" height="715" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/JBL-Ultra-Portable-Waterproof-Dustproof-Built/dp/B0CTNWKGM2" target="_blank" rel="nofollow" data-dimension112="72d41544-90db-11f1-9a21-cb8caaa7cb47" data-action="Deal Block" data-label="Go 4 Bluetooth Speaker" data-dimension48="Go 4 Bluetooth Speaker" data-dimension25="$39">Go 4 Bluetooth Speaker: was $49 now $39</a></strong><br>Our <a href="https://www.tomsguide.com/audio/bluetooth-speakers/jbl-go-4-review">JBL Go 4 review</a> determined that this is the perfect portable Bluetooth speaker for those on a budget. Small enough to take traveling, it still packs enough performance to soundtrack small gatherings while bringing useful features like IP67 water and dust protection, and multi-point connectivity. It also comes in a variety of fun colors.<a class="view-deal button" href="https://www.amazon.com/JBL-Ultra-Portable-Waterproof-Dustproof-Built/dp/B0CTNWKGM2" target="_blank" rel="nofollow" data-dimension112="72d41544-90db-11f1-9a21-cb8caaa7cb47" data-action="Deal Block" data-label="Go 4 Bluetooth Speaker" data-dimension48="Go 4 Bluetooth Speaker" data-dimension25="$39">View Deal</a></p></div><div class="product"><a data-dimension112="72d415a8-90db-11f1-8c70-e79050bc1723" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59" href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1190px;"><p class="vanilla-image-block" style="padding-top:113.61%;"><img id="kYA8RY5ohsqVvc9Ki7zKfL" name="JBL Tune 720BT.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kYA8RY5ohsqVvc9Ki7zKfL.jpg" mos="" align="middle" fullscreen="" width="1190" height="1352" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow" data-dimension112="72d415a8-90db-11f1-8c70-e79050bc1723" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59">Tune 720BT: was $89 now $59</a></strong><br>These budget cans pack in more features, although you'll miss out on the ANC you'll get with some of the other options on this list. Still, these headphones have a massive 76 hour battery, so they'll last you for a long, long time. Thick cushioning provides a comfortable fit for long periods of wear.<a class="view-deal button" href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow" data-dimension112="72d415a8-90db-11f1-8c70-e79050bc1723" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59">View Deal</a></p></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/19-best-headphone-earbud-and-speaker-deals-on-amazon-save-up-to-50-percent-on-bose-apple-jbl-and-more</link>
                                                                            <description>
                            <![CDATA[ Amazon is knocking up to 50% off speakers and headphones. Check out my favorite affordable picks from Bose, Apple, Sony and more. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">SqmL5kiZdU25SEWiQddeSN</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/9GA2y8LKzwDmfASLpKgTA4-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 05 Aug 2026 15:09:47 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Audio]]></category>
                                                                                                                    <dc:creator><![CDATA[ Olivia Halevy ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/YAg7Fmh9dGKbg3hULBMWiU.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Olivia is a Deals Writer at Tom’s Guide. She loves all things lifestyle, shopping and sourcing the best deals from some of the top brands, retailers and websites around. From fashion and beauty to smart home and tech, she has an eye for finding practical, stylish, and reasonably-priced items that help make life simpler (and a little more aesthetically-pleasing).&amp;nbsp;Olivia has over five years of experience writing lifestyle, commerce, and branded content for &lt;a href=&quot;http://patch.com/&quot;&gt;Patch.com&lt;/a&gt;. She&#039;s covered major shopping holidays like Amazon Prime Day, Black Friday and Cyber Monday. In her free time, she enjoys trying out new exercise classes, binge-watching the latest viral TV shows, cooking, spending time with family and taking long walks with her dog, Gus.&amp;nbsp;&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/9GA2y8LKzwDmfASLpKgTA4-1280-80.jpg">
                                                            <media:credit><![CDATA[Amazon / edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[audio devices]]></media:description>                                                            <media:text><![CDATA[audio devices]]></media:text>
                                <media:title type="plain"><![CDATA[audio devices]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/9GA2y8LKzwDmfASLpKgTA4-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Summer is the perfect time to upgrade your audio setup with the high-quality headphones, earbuds or speakers you've had your eye on. Even better, Amazon is offering up to 50% off top-rated audio gear, making now a great time to save.</p><p>Amazon's sale has deals for every budget. Some of the <a href="https://www.tomsguide.com/us/best-headphones-deals,news-28645.html">best headphones </a>we've tested from brands like JBL, Bose, Apple, and Sony are discounted to as little as $38. You'll also find great savings on some of our favorite speakers, including <a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow">the Bose SoundLink Micro for just $99</a>. </p><p>If you're looking to upgrade your audio devices for summer travel, pool parties, beach days and more, keep scrolling. I've rounded up all my favorite audio device deals on Amazon that will get you the most bang for your buck! </p><h3 class="article-body__section" id="section-quick-links"><span>Quick Links </span></h3><ul><li><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>shop all deals on Amazon</strong></a></li><li><strong>Sony WH-CH520: </strong><a href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow"><strong>was $59 now $38</strong></a></li><li><strong>Beats Flex Wireless: </strong><a href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow"><strong>was $69 now $45</strong></a></li><li><strong>Apple AirPods 4: </strong><a href="https://www.amazon.com/dp/B0DGHMNQ5Z/" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Bose SoundLink Micro: </strong><a href="https://www.amazon.com/dp/B0FDLD34V5" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Beats Studio Pro: </strong><a href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" target="_blank" rel="nofollow"><strong>was $349 now $169</strong></a></li></ul><h3 class="article-body__section" id="section-bose-deals"><span>Bose Deals</span></h3><div class="product"><a data-dimension112="72d40a54-90db-11f1-8b44-b15be0f8e0c6" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1105px;"><p class="vanilla-image-block" style="padding-top:110.05%;"><img id="4S8VXWvZTpFMLYd8oiNBC7" name="Bose Micro" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4S8VXWvZTpFMLYd8oiNBC7.jpg" mos="" align="middle" fullscreen="" width="1105" height="1216" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow" data-dimension112="72d40a54-90db-11f1-8b44-b15be0f8e0c6" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99">SoundLink Micro Portable Bluetooth Speaker: was $129 now $99</a></strong><br>The SoundLink Micro is a compact, portable speaker you can take on the road. The Bluetooth speaker is waterproof (IPX7 certified) and offers up to 6 hours of play time off a single charge. It can also be paired with another SoundLink speaker to create a stereo mode.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0FDLD34V5?" target="_blank" rel="nofollow" data-dimension112="72d40a54-90db-11f1-8b44-b15be0f8e0c6" data-action="Deal Block" data-label="SoundLink Micro Portable Bluetooth Speaker" data-dimension48="SoundLink Micro Portable Bluetooth Speaker" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="72d40acc-90db-11f1-91e8-f935d50bbc46" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:75.11%;"><img id="fexRzMYRvYZTHGfbcUbFf8" name="Bose speaker" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/fexRzMYRvYZTHGfbcUbFf8.jpg" mos="" align="middle" fullscreen="" width="679" height="510" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow" data-dimension112="72d40acc-90db-11f1-91e8-f935d50bbc46" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119">SoundLink Flex (Gen 2): was $149 now $119</a></strong><br>The new Bose SoundLink (2nd gen) is a compact and powerful Bluetooth speaker with a durable design. This 1.2-pound speaker is water and dust proof and delivered great sound quality in our <a href="https://www.tomsguide.com/audio/bose-soundlink-flex-2-review" target="_blank">SoundLink Flex gen 2 review</a>. With 12 hours of battery life and $30 off, you have yourself a great deal.<a class="view-deal button" href="https://www.amazon.com/Bose-SoundLink-Bluetooth-Waterproof-Dustproof/dp/B0D6WB7BV6" target="_blank" rel="nofollow" data-dimension112="72d40acc-90db-11f1-91e8-f935d50bbc46" data-action="Deal Block" data-label="SoundLink Flex (Gen 2)" data-dimension48="SoundLink Flex (Gen 2)" data-dimension25="$119">View Deal</a></p></div><div class="product"><a data-dimension112="72d40b3a-90db-11f1-80c2-6551ca6b366f" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1268px;"><p class="vanilla-image-block" style="padding-top:62.38%;"><img id="qDBpkjESq4Z3UsvHc4vEeX" name="bose open earbuds" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qDBpkjESq4Z3UsvHc4vEeX.jpg" mos="" align="middle" fullscreen="" width="1268" height="791" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="72d40b3a-90db-11f1-80c2-6551ca6b366f" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">Ultra Open Earbuds: was $299 now $199</a></strong><br>The Bose Ultra Open perfected the open earbud formula. They have a comfortable, non-invasive fit, solid battery life, and they sound better than you might expect from open buds. They got <a href="https://www.tomsguide.com/audio/earbuds/bose-ultra-open-earbuds">4 stars in our review</a>, where we loved their simple controls and surprising lack of sound leakage. <a class="view-deal button" href="https://www.amazon.com/dp/B0CPFV77W4/" target="_blank" rel="nofollow" data-dimension112="72d40b3a-90db-11f1-80c2-6551ca6b366f" data-action="Deal Block" data-label="Ultra Open Earbuds" data-dimension48="Ultra Open Earbuds" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="72d40ba8-90db-11f1-8d93-678f9f1d8429" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$309" href="https://www.amazon.com/dp/B0CVL1K7DX/" 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="dT2cC2J8saDETsZvrBwfqk" name="Bose_SoundLink_Max_square.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/dT2cC2J8saDETsZvrBwfqk.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>With big sound and a fashionable look, the SoundLink Max is a great portable companion for taking on the go. 20 hours of battery life is a big bonus as well, giving you plenty of listening time. <a class="view-deal button" href="https://www.amazon.com/dp/B0CVL1K7DX/" target="_blank" rel="nofollow" data-dimension112="72d40ba8-90db-11f1-8d93-678f9f1d8429" data-action="Deal Block" data-label="SoundLink Max" data-dimension48="SoundLink Max" data-dimension25="$309">View Deal</a></p></div><div class="product"><a data-dimension112="72d40c16-90db-11f1-ac27-89f31c84db62" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$339" href="https://www.amazon.com/dp/B0CCZ1L489?" 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="g64SDV3bmD2tcgopiiNgP6" name="Bose QuietComfort Ultra headphones Versus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/g64SDV3bmD2tcgopiiNgP6.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Bose QuietComfort Ultra Headphones get you epic ANC, super sound, and the most comfortable fit this side of wrapping your head in a snood that plays music. 24 hours of battery life isn't the best, but they make up for it with just about everything else.<a class="view-deal button" href="https://www.amazon.com/dp/B0CCZ1L489?" target="_blank" rel="nofollow" data-dimension112="72d40c16-90db-11f1-ac27-89f31c84db62" data-action="Deal Block" data-label="QuietComfort Ultra Headphones" data-dimension48="QuietComfort Ultra Headphones" data-dimension25="$339">View Deal</a></p></div><h3 class="article-body__section" id="section-apple-deals"><span>Apple Deals</span></h3><div class="product"><a data-dimension112="72d40cfc-90db-11f1-a077-43910f515ad0" data-action="Deal Block" data-label="AirPods 4" data-dimension48="AirPods 4" data-dimension25="$99" href="https://www.amazon.com/dp/B0DGHMNQ5Z/" 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="kTKCG8gCBjg4vScr6CMgoN" name="AirPods 4 BG image.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kTKCG8gCBjg4vScr6CMgoN.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods 4 are about as barebones as AirPods come, but they're still packed with features. Spatial Audio is on board for example, something you'd have to pay more for on the old model. 5 hours of battery isn't the very best, but they're a great on boarding place for the AirPods line.<a class="view-deal button" href="https://www.amazon.com/dp/B0DGHMNQ5Z/" target="_blank" rel="nofollow" data-dimension112="72d40cfc-90db-11f1-a077-43910f515ad0" data-action="Deal Block" data-label="AirPods 4" data-dimension48="AirPods 4" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="72d40da6-90db-11f1-8c6e-cb92d8ce214a" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$199" href="https://www.amazon.com/Apple-Cancellation-Translation-Headphones-High-Fidelity/dp/B0FQFB8FMG" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:842px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="FYzcjQSdtuwidVacXmMNL5" name="AirPods Pro 3.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FYzcjQSdtuwidVacXmMNL5.jpg" mos="" align="middle" fullscreen="" width="842" height="842" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods Pro 3 bring the heat with 10 hours of battery life, improved sound, and superior active noise canceling. In our <a href="https://www.tomsguide.com/audio/airpods/apple-airpods-pro-3-review" data-dimension112="72d40da6-90db-11f1-8c6e-cb92d8ce214a" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$199">AirPods Pro 3 review</a> we said the Editor's Choice buds deliver the upgrade iPhone users have been waiting for with improved battery life, stronger noise cancellation, and smart new features like Live Translation, which delivers real-time translations to your ears. <a class="view-deal button" href="https://www.amazon.com/Apple-Cancellation-Translation-Headphones-High-Fidelity/dp/B0FQFB8FMG" target="_blank" rel="nofollow" data-dimension112="72d40da6-90db-11f1-8c6e-cb92d8ce214a" data-action="Deal Block" data-label="AirPods Pro 3" data-dimension48="AirPods Pro 3" data-dimension25="$199">View Deal</a></p></div><div class="product"><a data-dimension112="72d40e1e-90db-11f1-898d-ef286b0e1a5e" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$449" href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS72GZJ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1356px;"><p class="vanilla-image-block" style="padding-top:110.62%;"><img id="augarq3rWrbuXENFrmUU88" name="airpods max 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/augarq3rWrbuXENFrmUU88.jpg" mos="" align="middle" fullscreen="" width="1356" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The AirPods Max 2 are Apple's latest flagship over-ears, with hi-res audio playback via USB-C and Bluetooth, seamless Siri integration and smart features like Live Translation, intuitive controls, and a sleek, iconic design.<a class="view-deal button" href="https://www.amazon.com/Apple-Headphones-Cancellation-Personalized-Translation/dp/B0GSS72GZJ/" target="_blank" rel="nofollow" data-dimension112="72d40e1e-90db-11f1-898d-ef286b0e1a5e" data-action="Deal Block" data-label="AirPods Max 2" data-dimension48="AirPods Max 2" data-dimension25="$449">View Deal</a></p></div><h3 class="article-body__section" id="section-beats-deals"><span>Beats Deals</span></h3><div class="product"><a data-dimension112="72d40f22-90db-11f1-9904-73872852650a" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45" href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:103.68%;"><img id="FGXTiRGeZrNnRV5M5SvbR9" name="Beats Flex" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/FGXTiRGeZrNnRV5M5SvbR9.jpg" mos="" align="middle" fullscreen="" width="679" height="704" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow" data-dimension112="72d40f22-90db-11f1-9904-73872852650a" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45">Flex Wireless: was $69 now $45</a></strong><br>While they aren't <em>technically </em>true wireless, on account of the wire that connects the buds, the Flex are a great pair of fitness earbuds. They're super comfortable thanks to their lightweight, and this $24 saving is a solid discount.<a class="view-deal button" href="https://www.amazon.com/Beats-Flex-Wireless-Earphones-Built/dp/B08L6ZYW21/" target="_blank" rel="nofollow" data-dimension112="72d40f22-90db-11f1-9904-73872852650a" data-action="Deal Block" data-label="Flex Wireless" data-dimension48="Flex Wireless" data-dimension25="$45">View Deal</a></p></div><div class="product"><a data-dimension112="72d40fcc-90db-11f1-a2e6-c960b6c6c5f9" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99" href="https://www.amazon.com/dp/B0C2W1KLSS/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:107.51%;"><img id="Nu3NHDhGaSDd5XGizimnyT" name="Studio Buds plus" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Nu3NHDhGaSDd5XGizimnyT.jpg" mos="" align="middle" fullscreen="" width="679" height="730" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Studio Buds are a wonderful little pair of buds, with some big improvements over the last model. 9 hours of battery life is exemplary, extended to 36 hours with the charging case, and they're some of the most comfortable wireless in-ears around. In our <a href="https://www.tomsguide.com/reviews/beats-studio-buds-plus" data-dimension112="72d40fcc-90db-11f1-a2e6-c960b6c6c5f9" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99">Beats Studio Buds Plus review</a>, we gave them 4 stars thanks to their great design and sound.<a class="view-deal button" href="https://www.amazon.com/dp/B0C2W1KLSS/" target="_blank" rel="nofollow" data-dimension112="72d40fcc-90db-11f1-a2e6-c960b6c6c5f9" data-action="Deal Block" data-label="Studio Buds Plus" data-dimension48="Studio Buds Plus" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="72d41076-90db-11f1-a9f9-730c5d8266e9" data-action="Deal Block" data-label="Pill" data-dimension48="Pill" data-dimension25="$99" href="https://www.amazon.com/gp/aw/d/B0D4STT6C2/" 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="DG5WgcSHFZLZ9WcXutJuLT" name="Beats Pill" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DG5WgcSHFZLZ9WcXutJuLT.jpg" mos="" align="middle" fullscreen="" width="1000" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In our <a href="https://www.tomsguide.com/audio/beats-pill-2024-tested-5-things-i-like-3-that-i-dont" data-dimension112="72d41076-90db-11f1-a9f9-730c5d8266e9" data-action="Deal Block" data-label="Pill" data-dimension48="Pill" data-dimension25="$99">Beats Pill hands-on review</a>, we found a speaker that has (as you might expect) some massive bass and a slick look. Battery life is respectable at 24 hours, and its waterproofing makes it perfect for pool parties and outdoor use.<a class="view-deal button" href="https://www.amazon.com/gp/aw/d/B0D4STT6C2/" target="_blank" rel="nofollow" data-dimension112="72d41076-90db-11f1-a9f9-730c5d8266e9" data-action="Deal Block" data-label="Pill" data-dimension48="Pill" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="72d41134-90db-11f1-af04-efa2390bbb34" data-action="Deal Block" data-label="Solo 4" data-dimension48="Solo 4" data-dimension25="$129" href="https://www.amazon.com/dp/B0CZPHPJLN/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:717px;"><p class="vanilla-image-block" style="padding-top:154.25%;"><img id="ZDYGygzUQQsApUVGtH5RBe" name="510fGxoTsTL._AC_SL1500_.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ZDYGygzUQQsApUVGtH5RBe.jpg" mos="" align="middle" fullscreen="" width="717" height="1106" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Solo 4 might not feature noise canceling, but they make up for it by sounding pretty good. They're comfortable as well thanks to Beats new foam, and they fold up super small so that they can fit into any bag. There's also loads of battery life — 50 hours, to be exact.<a class="view-deal button" href="https://www.amazon.com/dp/B0CZPHPJLN/" target="_blank" rel="nofollow" data-dimension112="72d41134-90db-11f1-af04-efa2390bbb34" data-action="Deal Block" data-label="Solo 4" data-dimension48="Solo 4" data-dimension25="$129">View Deal</a></p></div><div class="product"><a data-dimension112="72d41198-90db-11f1-9cb7-0dd17c54575f" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169" href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" 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="WbdEkaq3pnr5qszwU9U9aX" name="Beats Studio Pro on white.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WbdEkaq3pnr5qszwU9U9aX.jpg" mos="" align="middle" fullscreen="" width="1000" height="1000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Beats Studio Pro are specifically designed to bring back that thumping bass Beats is known for. They offer improved active noise cancelation and boosted battery life (up to 40 hours with ANC turned off). There's also Spatial Audio support and Beats' signature strong brand look. However, our <a href="https://www.tomsguide.com/reviews/beats-studio-pro-review" data-dimension112="72d41198-90db-11f1-9cb7-0dd17c54575f" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169">Beats Studio Pro review</a> found the clamping force high, which may affect comfort levels for some wearers. Originally priced at $349, they've now on sale for over half off.<a class="view-deal button" href="https://www.amazon.com/Beats-Studio-Pro-Personalized-Compatibility/dp/B0C8PR4W22?" target="_blank" rel="nofollow" data-dimension112="72d41198-90db-11f1-9cb7-0dd17c54575f" data-action="Deal Block" data-label="Studio Pro" data-dimension48="Studio Pro" data-dimension25="$169">View Deal</a></p></div><h3 class="article-body__section" id="section-sony-deals"><span>Sony Deals</span></h3><div class="product"><a data-dimension112="72d41256-90db-11f1-b14d-178f6e757603" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38" href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:510px;"><p class="vanilla-image-block" style="padding-top:154.90%;"><img id="bzH89XJXSHScmHcf8rgiyP" name="Sony_WH-C520_Headphones_cappucino_deal_block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bzH89XJXSHScmHcf8rgiyP.jpg" mos="" align="middle" fullscreen="" width="510" height="790" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Our <a href="https://www.tomsguide.com/audio/over-ear-headphones/sony-wh-ch520-review" data-dimension112="72d41256-90db-11f1-b14d-178f6e757603" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38">Sony WH-CH520 headphones review</a> said these headphones "deliver on battery life and performance."  They're 360 Reality Audio compatible for more 3D sound and you can customize EQ in the Sony Headphones app. Although there's no active noise cancelling function, they have built-in mics for hands-free voice and video calls and noise suppression processing to keep your voice sounding clear in noisy environments. They're also rated for up to 50 hours of battery life.<a class="view-deal button" href="https://www.amazon.com/dp/B0BS1RT9S2" target="_blank" rel="nofollow" data-dimension112="72d41256-90db-11f1-b14d-178f6e757603" data-action="Deal Block" data-label="WH-CH520" data-dimension48="WH-CH520" data-dimension25="$38">View Deal</a></p></div><div class="product"><a data-dimension112="72d412e2-90db-11f1-9d1c-d92b5bdbddac" data-action="Deal Block" data-label="SRS-XB100" data-dimension48="SRS-XB100" data-dimension25="$53" href="https://www.amazon.com/Sony-Lightweight-Super-Compact-Extra-Durable-Waterproof/dp/B0C296VD7T?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1428px;"><p class="vanilla-image-block" style="padding-top:105.04%;"><img id="2HoERCRCD9zg7NesyDvkZC" name="sony-srs-xb13-speaker.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/2HoERCRCD9zg7NesyDvkZC.jpg" mos="" align="middle" fullscreen="" width="1428" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Sony-Lightweight-Super-Compact-Extra-Durable-Waterproof/dp/B0C296VD7T?" target="_blank" rel="nofollow" data-dimension112="72d412e2-90db-11f1-9d1c-d92b5bdbddac" data-action="Deal Block" data-label="SRS-XB100" data-dimension48="SRS-XB100" data-dimension25="$53">SRS-XB100: was $59 now $53</a></strong><br>One of the best Bluetooth speakers in terms of portability is on sale right now at Amazon. Believe it or not, this 0.6-pound speaker packs a punch. The orange, light grey and black colors are all included in the promotion. <a class="view-deal button" href="https://www.amazon.com/Sony-Lightweight-Super-Compact-Extra-Durable-Waterproof/dp/B0C296VD7T?" target="_blank" rel="nofollow" data-dimension112="72d412e2-90db-11f1-9d1c-d92b5bdbddac" data-action="Deal Block" data-label="SRS-XB100" data-dimension48="SRS-XB100" data-dimension25="$53">View Deal</a></p></div><div class="product"><a data-dimension112="72d41418-90db-11f1-8e6d-f3f948646660" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248" href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1509px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="zpDUbSJksxwfgT3KDaf4WS" name="Sony WF-1000XM5.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zpDUbSJksxwfgT3KDaf4WS.jpg" mos="" align="middle" fullscreen="" width="1509" height="1509" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow" data-dimension112="72d41418-90db-11f1-8e6d-f3f948646660" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248">WF-1000XM5: was $329 now $248</a></strong><br>These are some of the best buds that you can buy right now. They sound excellent, with some wickedly detailed sound with plenty of low-end grunt, and feature a very comfortable fit. We gave them 4.5 stars in <a href="https://www.tomsguide.com/reviews/sony-wf-1000xm5">our review</a> because of their great ANC, which manages to block out almost any noise you could throw at them. This isn't <em>quite</em> their lowest price ever, but it's very close.<a class="view-deal button" href="https://www.amazon.com/dp/B0C33XXS56/" target="_blank" rel="nofollow" data-dimension112="72d41418-90db-11f1-8e6d-f3f948646660" data-action="Deal Block" data-label="WF-1000XM5" data-dimension48="WF-1000XM5" data-dimension25="$248">View Deal</a></p></div><div class="product"><a data-dimension112="72d4147c-90db-11f1-a163-fbd2d05412b6" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248" href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B0BXYCS74H?" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1381px;"><p class="vanilla-image-block" style="padding-top:108.62%;"><img id="TWLQcjD9oitTFCdEHsgTe5" name="Sony WH-1000XM5 deal block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TWLQcjD9oitTFCdEHsgTe5.jpg" mos="" align="middle" fullscreen="" width="1381" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B0BXYCS74H?" target="_blank" rel="nofollow" data-dimension112="72d4147c-90db-11f1-a163-fbd2d05412b6" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248">WH-1000XM5: was $399 now $248</a></strong><br>The XM5s are Sony's premium noise-canceling headphones and our pick for the best headphones on the market. The large, over-ear cushions are incredibly comfortable and the active noise cancelation is excellent. Plus, despite ANC being a drain on battery life, these amazing headphones still give you up to 30 hours with ANC enabled. You can push that up to 40 hours of battery life with ANC turned off.<a class="view-deal button" href="https://www.amazon.com/Sony-WH-1000XM5-Headphones-Hands-Free-WH1000XM5/dp/B0BXYCS74H?" target="_blank" rel="nofollow" data-dimension112="72d4147c-90db-11f1-a163-fbd2d05412b6" data-action="Deal Block" data-label="WH-1000XM5" data-dimension48="WH-1000XM5" data-dimension25="$248">View Deal</a></p></div><h3 class="article-body__section" id="section-jbl-deals"><span>JBL Deals</span></h3><div class="product"><a data-dimension112="72d41544-90db-11f1-9a21-cb8caaa7cb47" data-action="Deal Block" data-label="Go 4 Bluetooth Speaker" data-dimension48="Go 4 Bluetooth Speaker" data-dimension25="$39" href="https://www.amazon.com/JBL-Ultra-Portable-Waterproof-Dustproof-Built/dp/B0CTNWKGM2" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:679px;"><p class="vanilla-image-block" style="padding-top:105.30%;"><img id="PfFcSd7yE4rJP5xxATqJ3e" name="JBL Go" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/PfFcSd7yE4rJP5xxATqJ3e.jpg" mos="" align="middle" fullscreen="" width="679" height="715" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/JBL-Ultra-Portable-Waterproof-Dustproof-Built/dp/B0CTNWKGM2" target="_blank" rel="nofollow" data-dimension112="72d41544-90db-11f1-9a21-cb8caaa7cb47" data-action="Deal Block" data-label="Go 4 Bluetooth Speaker" data-dimension48="Go 4 Bluetooth Speaker" data-dimension25="$39">Go 4 Bluetooth Speaker: was $49 now $39</a></strong><br>Our <a href="https://www.tomsguide.com/audio/bluetooth-speakers/jbl-go-4-review">JBL Go 4 review</a> determined that this is the perfect portable Bluetooth speaker for those on a budget. Small enough to take traveling, it still packs enough performance to soundtrack small gatherings while bringing useful features like IP67 water and dust protection, and multi-point connectivity. It also comes in a variety of fun colors.<a class="view-deal button" href="https://www.amazon.com/JBL-Ultra-Portable-Waterproof-Dustproof-Built/dp/B0CTNWKGM2" target="_blank" rel="nofollow" data-dimension112="72d41544-90db-11f1-9a21-cb8caaa7cb47" data-action="Deal Block" data-label="Go 4 Bluetooth Speaker" data-dimension48="Go 4 Bluetooth Speaker" data-dimension25="$39">View Deal</a></p></div><div class="product"><a data-dimension112="72d415a8-90db-11f1-8c70-e79050bc1723" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59" href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1190px;"><p class="vanilla-image-block" style="padding-top:113.61%;"><img id="kYA8RY5ohsqVvc9Ki7zKfL" name="JBL Tune 720BT.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/kYA8RY5ohsqVvc9Ki7zKfL.jpg" mos="" align="middle" fullscreen="" width="1190" height="1352" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow" data-dimension112="72d415a8-90db-11f1-8c70-e79050bc1723" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59">Tune 720BT: was $89 now $59</a></strong><br>These budget cans pack in more features, although you'll miss out on the ANC you'll get with some of the other options on this list. Still, these headphones have a massive 76 hour battery, so they'll last you for a long, long time. Thick cushioning provides a comfortable fit for long periods of wear.<a class="view-deal button" href="https://www.amazon.com/dp/B0CS8WVRLQ/" target="_blank" rel="nofollow" data-dimension112="72d415a8-90db-11f1-8c70-e79050bc1723" data-action="Deal Block" data-label="Tune 720BT" data-dimension48="Tune 720BT" data-dimension25="$59">View Deal</a></p></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Panasonic RB-F10 review: The earbuds I’ve been relying on for working out and the office ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Open-earbuds are the <a href="https://www.tomsguide.com/wellness/fitness/best-workout-headphones">best workout earbuds</a>. In my mind, this is a fact. But how do you know which ones to buy? For me, the Panasonic RB-F10 are an easy recommendation. They’re lightweight and comfortable for workouts and all-day wear, and have a well-balanced, bright sound that will please most listeners. The call quality is great too, and the 25 hours of battery life lasted me for days on end. </p><p>But they’re not without their flaws. The bass performance lacks punch, and elements like drums and cymbals in the midrange are sometimes lost in the background, but this is to be expected when listening with open earbuds. Unfortunately, there’s no companion app for EQ customization to fix these qualms. The buds only have an IPX4 durability rating, which means they have no dust protection. </p><p>To learn more about the open-earbuds, read my full Panasonic RB-F10 review. </p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-specs"><span>Panasonic RB-F10 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/Panasonic-Headphones-Microphone-Multipoint-Connection/dp/B0GX2N2T63" target="_blank" rel="nofollow">$99</a> / <a href="https://www.amazon.co.uk/Panasonic-RB-F10DE-K-Headphones-Bluetooth-Microphone-Black/dp/B0F9JZXF6B" target="_blank" rel="nofollow">£79</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>17mm drivers</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20 - 20,000 Hz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IPX4</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.4</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>Yes, 2 devices</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>25 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Weight (per earbud)</strong></p></td><td  ><p>0.31 oz</p></td></tr><tr><td class="firstcol " ><p><strong>Weight (charging case)</strong></p></td><td  ><p>1.27 oz</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions (per earbud)</strong></p></td><td  ><p>1.8 x 1.6 x 0.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions (charging case)</strong></p></td><td  ><p>2.2 x 2.2 x 1.2 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black or Beige</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-panasonic-rb-f10-review-price-availability"><span>Panasonic RB-F10 review: Price & availability</span></h2><p>The Panasonic RB-F10 open-earbuds are available to purchase from <a href="https://www.amazon.com/Panasonic-Headphones-Microphone-Multipoint-Connection/dp/B0GX2N2T63" target="_blank" rel="nofollow">Amazon U.S. for $99</a> and from <a href="https://www.amazon.co.uk/Panasonic-RB-F10DE-K-Headphones-Bluetooth-Microphone-Black/dp/B0F9JZXF6B" target="_blank" rel="nofollow">Amazon U.K. for £79</a>. You can pick up the buds in Black or Beige, both of which look slick and understated. </p><p>These are hook-style open earbuds, like the <a href="https://www.tomsguide.com/audio/earbuds/beyerdynamic-amiron-200-review">Beyerdynamic Amiron 200</a>, which are firm favorites of mine. The Beyerdynamics cost a little more than the Panasonic buds at $199, but the sound from the Beyerdynamics is truly unmatched in the world of open-earbuds. That said, the Panasonic buds fit excellently and are incredibly comfortable, just like the Beyerdynamics, for half the price. </p><p>In the same price range are the <a href="https://www.tomsguide.com/audio/earbuds/jlab-epic-open-sport-review">JLab Epic Open Sport</a> buds for $114, which are secure even with intense movement. However, their hook-style fitment won’t be for everyone. So it’s worth checking out the <a href="https://www.tomsguide.com/audio/earbuds/earfun-clip-2-review">EarFun Clip 2</a> for $99. As the name suggests, these clip to the outside of your ear. This style is better suited to glasses wearers too, which I appreciate.</p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-design"><span>Panasonic RB-F10 review: Design </span></h2><p>The Panasonic RB-F10 open-earbuds look great. I often find hook-style buds a little jarring, as they can look bulky on the outer ear, but I really liked the look of these buds. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gtFCgLrCThYGLiden3Y57k" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/gtFCgLrCThYGLiden3Y57k.jpg" 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 earbuds and case have a durability rating of IPX4, meaning they’re splashproof and can withstand splashes from any direction. However, they don’t feature any dust protection, meaning you can’t take them to the beach or work out anywhere dusty. This makes them a great option for running in some very light rain, but for anything more I would give these buds a miss and go for something with a higher durability rating like the EarFun Clip 2, which are IP55. </p><p>It’s great that these buds have wear detection too, so I’ve been able to pause my music just by taking them on and off without worrying about losing my place in my music or audiobook. </p><h3 id="comfort-9">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JxDd7kpJdK7RKxxRMyHd8k" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/JxDd7kpJdK7RKxxRMyHd8k.jpg" 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 Panasonic RB-F10 earbuds are very comfortable to wear for extended periods. I wore them for a whole evening while running errands before going for a run and wasn’t bothered by them at all. Each earbud weighs 0.31 ounces, making them lighter than the Beyerdynamic Amiron 200, which weigh 0.4 ounces. </p><h2 id="controls-14">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="PDkRzsm4SFENn7ynwYqS7k" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/PDkRzsm4SFENn7ynwYqS7k.jpg" 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 Panasonic RB-F10 have touch-sensitive controls. These are very sensitive, as even brushing them when I tucked my hair behind my ear set them off. However, there is a slight delay of about a second between tapping and the action. This didn’t impact my listening but was annoying.</p><h3 id="connectivity-10">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NG9WzfjJvzkCwnwf9kiNHj" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/NG9WzfjJvzkCwnwf9kiNHj.jpg" 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 Panasonic RB-F10 open-earbuds use Bluetooth 5.4 to connect to your streaming device. They have a 33-foot operating distance, so I was able to leave my phone in the house and head out to the backyard to get on with some yardwork without losing the connection.</p><p>You can connect the earbuds to two devices thanks to multipoint connectivity. This made it easy for me to switch between listening to music with my phone and listening to work calls on my computer.</p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-sound-quality"><span>Panasonic RB-F10 review: Sound quality</span></h2><p>The Panasonic RB-F10 earbuds have a very well-balanced sound for open-earbuds. They suffer in the bass department, but that’s to be expected because of the open design, which cannot deliver bass to the ear in the same way. However, I was disappointed by the lack of EQ customization. Of course, this isn’t essential, but it’s a feature available from most audio brands, including Beyerdynamic, Soundcore, and Sony — the list goes on. Having EQ customization from an app would have allowed me to improve some of the problem frequencies of the buds. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6LXxUzYYUzLbeTyc7zcvbj" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/6LXxUzYYUzLbeTyc7zcvbj.jpg" 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 evaluate bass performance, I listened to <em>Sympathy is a Knife </em>by Charli XCX, which is driven by deep synth. I’ll start by saying the Panasonic buds performed a lot better than other open-earbuds I’ve tested. There were no issues with distortion or crackling — but I wasn't exactly impressed either. The bass wasn’t powerful. It sounded fine, but lacked the power that I’ve come to expect from this track. </p><p>I then listened to <em>Na Na Na </em>by My Chemical Romance, a song with very prominent drums, to judge the mids performance. Again, I was a little underwhelmed. While it wasn’t awful, I felt the drums and cymbals became lost in the background and sounded weak. I also listened to <em>Exile</em> by Taylor Swift. Here, the piano sounded crisp and clear, so it’s clear these earbuds thrive with simpler songs. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JsqmvcAzKQ4xLW7y3sP6aj" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/JsqmvcAzKQ4xLW7y3sP6aj.jpg" 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>Finally, I listened to <em>Next To Me </em>by Kingfishr. This is an airy folk track rooted in its vocal performance and acoustic guitar. The harmonies were prominent throughout, and the whole track sounded perfectly bright. This is where the earbuds thrived. The guitar in the background was prominent, and the harmonica was crisp when it came in.</p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-call-quality"><span>Panasonic RB-F10 review: Call quality</span></h2><p>The Panasonic RB-F10 earbuds have become a permanent fixture on my desk for taking calls. To make sure I sounded crystal clear, I called my mom, which is my standardized test. Despite having a lot of traffic whizzing past right outside the window of my home office, she could only hear my voice. This showed just how impressive the two beamforming microphones are for environmental noise cancellation. </p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-battery-life"><span>Panasonic RB-F10 review: Battery life</span></h2><p>The Panasonic RB-F10 open-earbuds have a total battery life of 25 hours with the charging case. This is more than enough to get me through a couple of days before needing a charge. But if you need more, buds like the EarFun Clip 2 can offer 40 hours of battery. Each of the Panasonic earbuds holds seven hours of battery life, which is enough for me when I’m sitting at my desk all day long or heading out for a run in the evening.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="EA88XVr4xioA7JzdP9dT6k" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/EA88XVr4xioA7JzdP9dT6k.jpg" 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 charging case takes two and a half hours to fully charge via USB-C, but if you’re in a rush you can get an hour of listening with 15 minutes of charging, which I found particularly useful before heading out on a run. </p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-verdict"><span>Panasonic RB-F10 review: Verdict</span></h2><p>The Panasonic RB-F10 open-earbuds have become a staple on my desk for ambient awareness at work and are also great for workouts. The buds are lightweight and comfortable  — I can wear them all day long, and the 25-hour battery life meant I wasn’t worried about running out of juice. The call quality sounds great, and the audio is well-balanced for open-earbuds — bright audio quality made them lovely to listen to my music with. </p><p>While I enjoyed the overall sound of the buds, it’s worth noting that the bass lacks punch and mid-range frequencies such as drums and cymbals were sometimes lost in the background. This could have been fixed with some EQ customization, but unfortunately there’s no compatible app to pair the earbuds with. However, this is to be expected with open-earbuds due to their design.</p><p>I’d recommend these buds to anyone who wants to try out open earbuds for working out or staying aware in the office.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/earbuds/panasonic-rb-f10-review</link>
                                                                            <description>
                            <![CDATA[ The Panasonic RB-F10 open-earbuds are a great option for anyone who needs reliable buds for the office or working out. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ggzDJjJ395ZJPrRKRqrLY4</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/JxDd7kpJdK7RKxxRMyHd8k-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 03 Aug 2026 09:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Earbuds]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></category>
                                                                                                <author><![CDATA[ ashley.thieme@futurenet.com (Ashley Thieme) ]]></author>                    <dc:creator><![CDATA[ Ashley Thieme ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3AWovHjApwuNrSGRS6WBcL.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ashley Thieme is a staff writer on the Reviews team at Tom’s Guide where she tests out the latest tech so you can know what’s going to be worth your time and money. She has a master’s degree in Magazine Journalism and a bachelor’s degree in Journalism, Media and Sociology from Cardiff University. She has bylines in titles including Women’s Health UK, sharing the latest health and wellness news stories, and Virgin Radio UK, providing the latest entertainment news and working on celebrity interviews. She has experience reporting on a variety of topics including music, literature, motorsport, entertainment and health. In previously published work, she has reviewed live music events, books, and wellness products. She values the importance of tech enhancing your life rather than taking over, and as a music fanatic, she is always looking for the best way to listen to new music releases. Discovering the top audio equipment that enhances sound quality and provides optimum comfort is what Ashley does best. In her spare time, Ashley enjoys hitting her reading goals on Goodreads by getting into the latest novels with a cup of tea as well as getting out in the Welsh mountains for a good hike on the weekend.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/JxDd7kpJdK7RKxxRMyHd8k-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Panasonic RB-F10 in beige photographed in front of a blue background]]></media:description>                                                            <media:text><![CDATA[Panasonic RB-F10 in beige photographed in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[Panasonic RB-F10 in beige photographed in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/JxDd7kpJdK7RKxxRMyHd8k-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Open-earbuds are the <a href="https://www.tomsguide.com/wellness/fitness/best-workout-headphones">best workout earbuds</a>. In my mind, this is a fact. But how do you know which ones to buy? For me, the Panasonic RB-F10 are an easy recommendation. They’re lightweight and comfortable for workouts and all-day wear, and have a well-balanced, bright sound that will please most listeners. The call quality is great too, and the 25 hours of battery life lasted me for days on end. </p><p>But they’re not without their flaws. The bass performance lacks punch, and elements like drums and cymbals in the midrange are sometimes lost in the background, but this is to be expected when listening with open earbuds. Unfortunately, there’s no companion app for EQ customization to fix these qualms. The buds only have an IPX4 durability rating, which means they have no dust protection. </p><p>To learn more about the open-earbuds, read my full Panasonic RB-F10 review. </p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-specs"><span>Panasonic RB-F10 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/Panasonic-Headphones-Microphone-Multipoint-Connection/dp/B0GX2N2T63" target="_blank" rel="nofollow">$99</a> / <a href="https://www.amazon.co.uk/Panasonic-RB-F10DE-K-Headphones-Bluetooth-Microphone-Black/dp/B0F9JZXF6B" target="_blank" rel="nofollow">£79</a></p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>17mm drivers</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20 - 20,000 Hz</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>IPX4</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.4</p></td></tr><tr><td class="firstcol " ><p><strong>Multipoint connectivity</strong></p></td><td  ><p>Yes, 2 devices</p></td></tr><tr><td class="firstcol " ><p><strong>Wear detection</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>25 hours</p></td></tr><tr><td class="firstcol " ><p><strong>Weight (per earbud)</strong></p></td><td  ><p>0.31 oz</p></td></tr><tr><td class="firstcol " ><p><strong>Weight (charging case)</strong></p></td><td  ><p>1.27 oz</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions (per earbud)</strong></p></td><td  ><p>1.8 x 1.6 x 0.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions (charging case)</strong></p></td><td  ><p>2.2 x 2.2 x 1.2 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Black or Beige</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-panasonic-rb-f10-review-price-availability"><span>Panasonic RB-F10 review: Price & availability</span></h2><p>The Panasonic RB-F10 open-earbuds are available to purchase from <a href="https://www.amazon.com/Panasonic-Headphones-Microphone-Multipoint-Connection/dp/B0GX2N2T63" target="_blank" rel="nofollow">Amazon U.S. for $99</a> and from <a href="https://www.amazon.co.uk/Panasonic-RB-F10DE-K-Headphones-Bluetooth-Microphone-Black/dp/B0F9JZXF6B" target="_blank" rel="nofollow">Amazon U.K. for £79</a>. You can pick up the buds in Black or Beige, both of which look slick and understated. </p><p>These are hook-style open earbuds, like the <a href="https://www.tomsguide.com/audio/earbuds/beyerdynamic-amiron-200-review">Beyerdynamic Amiron 200</a>, which are firm favorites of mine. The Beyerdynamics cost a little more than the Panasonic buds at $199, but the sound from the Beyerdynamics is truly unmatched in the world of open-earbuds. That said, the Panasonic buds fit excellently and are incredibly comfortable, just like the Beyerdynamics, for half the price. </p><p>In the same price range are the <a href="https://www.tomsguide.com/audio/earbuds/jlab-epic-open-sport-review">JLab Epic Open Sport</a> buds for $114, which are secure even with intense movement. However, their hook-style fitment won’t be for everyone. So it’s worth checking out the <a href="https://www.tomsguide.com/audio/earbuds/earfun-clip-2-review">EarFun Clip 2</a> for $99. As the name suggests, these clip to the outside of your ear. This style is better suited to glasses wearers too, which I appreciate.</p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-design"><span>Panasonic RB-F10 review: Design </span></h2><p>The Panasonic RB-F10 open-earbuds look great. I often find hook-style buds a little jarring, as they can look bulky on the outer ear, but I really liked the look of these buds. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="gtFCgLrCThYGLiden3Y57k" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/gtFCgLrCThYGLiden3Y57k.jpg" 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 earbuds and case have a durability rating of IPX4, meaning they’re splashproof and can withstand splashes from any direction. However, they don’t feature any dust protection, meaning you can’t take them to the beach or work out anywhere dusty. This makes them a great option for running in some very light rain, but for anything more I would give these buds a miss and go for something with a higher durability rating like the EarFun Clip 2, which are IP55. </p><p>It’s great that these buds have wear detection too, so I’ve been able to pause my music just by taking them on and off without worrying about losing my place in my music or audiobook. </p><h3 id="comfort-9">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JxDd7kpJdK7RKxxRMyHd8k" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/JxDd7kpJdK7RKxxRMyHd8k.jpg" 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 Panasonic RB-F10 earbuds are very comfortable to wear for extended periods. I wore them for a whole evening while running errands before going for a run and wasn’t bothered by them at all. Each earbud weighs 0.31 ounces, making them lighter than the Beyerdynamic Amiron 200, which weigh 0.4 ounces. </p><h2 id="controls-14">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="PDkRzsm4SFENn7ynwYqS7k" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/PDkRzsm4SFENn7ynwYqS7k.jpg" 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 Panasonic RB-F10 have touch-sensitive controls. These are very sensitive, as even brushing them when I tucked my hair behind my ear set them off. However, there is a slight delay of about a second between tapping and the action. This didn’t impact my listening but was annoying.</p><h3 id="connectivity-10">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NG9WzfjJvzkCwnwf9kiNHj" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/NG9WzfjJvzkCwnwf9kiNHj.jpg" 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 Panasonic RB-F10 open-earbuds use Bluetooth 5.4 to connect to your streaming device. They have a 33-foot operating distance, so I was able to leave my phone in the house and head out to the backyard to get on with some yardwork without losing the connection.</p><p>You can connect the earbuds to two devices thanks to multipoint connectivity. This made it easy for me to switch between listening to music with my phone and listening to work calls on my computer.</p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-sound-quality"><span>Panasonic RB-F10 review: Sound quality</span></h2><p>The Panasonic RB-F10 earbuds have a very well-balanced sound for open-earbuds. They suffer in the bass department, but that’s to be expected because of the open design, which cannot deliver bass to the ear in the same way. However, I was disappointed by the lack of EQ customization. Of course, this isn’t essential, but it’s a feature available from most audio brands, including Beyerdynamic, Soundcore, and Sony — the list goes on. Having EQ customization from an app would have allowed me to improve some of the problem frequencies of the buds. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6LXxUzYYUzLbeTyc7zcvbj" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/6LXxUzYYUzLbeTyc7zcvbj.jpg" 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 evaluate bass performance, I listened to <em>Sympathy is a Knife </em>by Charli XCX, which is driven by deep synth. I’ll start by saying the Panasonic buds performed a lot better than other open-earbuds I’ve tested. There were no issues with distortion or crackling — but I wasn't exactly impressed either. The bass wasn’t powerful. It sounded fine, but lacked the power that I’ve come to expect from this track. </p><p>I then listened to <em>Na Na Na </em>by My Chemical Romance, a song with very prominent drums, to judge the mids performance. Again, I was a little underwhelmed. While it wasn’t awful, I felt the drums and cymbals became lost in the background and sounded weak. I also listened to <em>Exile</em> by Taylor Swift. Here, the piano sounded crisp and clear, so it’s clear these earbuds thrive with simpler songs. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="JsqmvcAzKQ4xLW7y3sP6aj" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/JsqmvcAzKQ4xLW7y3sP6aj.jpg" 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>Finally, I listened to <em>Next To Me </em>by Kingfishr. This is an airy folk track rooted in its vocal performance and acoustic guitar. The harmonies were prominent throughout, and the whole track sounded perfectly bright. This is where the earbuds thrived. The guitar in the background was prominent, and the harmonica was crisp when it came in.</p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-call-quality"><span>Panasonic RB-F10 review: Call quality</span></h2><p>The Panasonic RB-F10 earbuds have become a permanent fixture on my desk for taking calls. To make sure I sounded crystal clear, I called my mom, which is my standardized test. Despite having a lot of traffic whizzing past right outside the window of my home office, she could only hear my voice. This showed just how impressive the two beamforming microphones are for environmental noise cancellation. </p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-battery-life"><span>Panasonic RB-F10 review: Battery life</span></h2><p>The Panasonic RB-F10 open-earbuds have a total battery life of 25 hours with the charging case. This is more than enough to get me through a couple of days before needing a charge. But if you need more, buds like the EarFun Clip 2 can offer 40 hours of battery. Each of the Panasonic earbuds holds seven hours of battery life, which is enough for me when I’m sitting at my desk all day long or heading out for a run in the evening.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="EA88XVr4xioA7JzdP9dT6k" name="Panasonic RB-F10" alt="Panasonic RB-F10 in beige photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/EA88XVr4xioA7JzdP9dT6k.jpg" 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 charging case takes two and a half hours to fully charge via USB-C, but if you’re in a rush you can get an hour of listening with 15 minutes of charging, which I found particularly useful before heading out on a run. </p><h2 class="article-body__section" id="section-panasonic-rb-f10-review-verdict"><span>Panasonic RB-F10 review: Verdict</span></h2><p>The Panasonic RB-F10 open-earbuds have become a staple on my desk for ambient awareness at work and are also great for workouts. The buds are lightweight and comfortable  — I can wear them all day long, and the 25-hour battery life meant I wasn’t worried about running out of juice. The call quality sounds great, and the audio is well-balanced for open-earbuds — bright audio quality made them lovely to listen to my music with. </p><p>While I enjoyed the overall sound of the buds, it’s worth noting that the bass lacks punch and mid-range frequencies such as drums and cymbals were sometimes lost in the background. This could have been fixed with some EQ customization, but unfortunately there’s no compatible app to pair the earbuds with. However, this is to be expected with open-earbuds due to their design.</p><p>I’d recommend these buds to anyone who wants to try out open earbuds for working out or staying aware in the office.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ These iClever Q950s might be the best kid headphones I’ve ever tried ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Headphones that are specifically labeled for children tend to feature a ton of drawbacks, especially when it comes to sound quality. That said, why we recommend specific kid headphones is because most of the ones we test feature volume limiters designed to protect little ears from future hearing damage or loss.</p><p>With that in mind, the iClever Q950 might be the best kid headphones I’ve ever tested. They’re comfortable and sound amazing for kids’ cans. No, they won’t replace the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best headphones</a> for adults, but a kid won’t complain, and even most grown-ups will enjoy listening to this set. Read my full iClever Q950 review to find out why.</p><h2 class="article-body__section" id="section-iclever-q950-review-specs"><span>iClever Q950 review: Specs</span></h2><div ><table><caption>iClever Q950 Specs</caption><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://iclever.com/products/iclever-q950-noise-cancelling-headphones" target="_blank" rel="nofollow">$69</a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Over ear</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>40mm</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz - 20kHz</p></td></tr><tr><td class="firstcol " ><p><strong>Safe volume limited</strong></p></td><td  ><p>80dBA</p></td></tr><tr><td class="firstcol " ><p><strong>Battery capacity</strong></p></td><td  ><p>500mAh</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Wired, 3.5mm cable or Bluetooth v6.0, USB-C charging port</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>9.9 oz (238 grams)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Blue/Purple</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-iclever-q950-review-price-availability"><span>iClever Q950 review: Price & availability</span></h2><p>The iClever Q950 cost <a href="https://www.amazon.com/dp/B0H1BLMC7R" target="_blank" rel="nofollow">$69 at Amazon</a>, but we’ve seen them a bit lower at $59 during sales events. Even at full price, these are pretty good value for money. </p><p>The Q950 competes with other iClever offerings like its colorful <a href="https://www.tomsguide.com/audio/headphones/iclever-auraa-headphones-hands-on">Auraa headphones</a> ($39) and the <a href="https://www.tomsguide.com/audio/headphones/these-stylish-ear-friendly-kid-headphones-promise-to-protect-your-childs-hearing-and-i-legit-agree">Puro Sound Lab BT2200s</a> ($99) or the extremely low-cost $20 <a href="https://www.tomsguide.com/audio/headphones/i-gave-my-kid-the-jbl-junior-320-headphones-they-look-cheap-but-sound-surprisingly-good">JBL Junior 320s</a>. I don’t think you’d go wrong with the other options, especially if you’re trying to save a buck, but personally, I’d recommend the Q950 as the middle ground. Plus, they sound better than the other offerings.</p><h2 class="article-body__section" id="section-iclever-q950-review-design"><span>iClever Q950 review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="EHwkPsehVbnHCFs4H4zcsU" name="iClever-q950--3" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/EHwkPsehVbnHCFs4H4zcsU.jpg" mos="" align="middle" fullscreen="" width="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>Compared to most kid headphones, the iClever Q950 look more sophisticated while also clearly being made for children. </p><p>Compared to the usual grays, blacks and whites that make up adult headphones, the Q950 are quite colorful but not quite as garish as they could be. Largely that has to do with the bright blue or purple hues. The holders have a pleasant metallic sheen. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="BYiYwTxEWr3qHXDLRJe8wT" name="iClever-q950--7" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/BYiYwTxEWr3qHXDLRJe8wT.jpg" mos="" align="middle" fullscreen="" width="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>Outside of colors, these are pretty durable headphones; I performed a couple of drop tests and twisted the headphones around a few times and never felt afraid that I was about to break them, though I’m sure an enterprising tot could destroy them.</p><p>They’re over-ear headphones with extendable ears that fold in, but can also turn out to lay flat if you want. They won’t disappear, but it gives you some flexibility, especially when trying to store them in a backpack. For extra protection, the Q950 does come with a bulky carry case that can hold charging wires and the 3.5mm wire. The case isn’t as bag-friendly, but you can stuff some Pokémon cards in there if you want.</p><h3 id="comfort-10">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="S9oHoTU68GAuMZ3wFzmGrU" name="iClever-q950--5" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/S9oHoTU68GAuMZ3wFzmGrU.jpg" mos="" align="middle" fullscreen="" width="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 comfort in mind, one of my personal bugaboos with over-ear headphones is the headband. If I notice the band during a listening session, it becomes grating. I never noticed it with the Q950, which is one of the highest compliments I can pay.</p><p>Despite my adult ears, the ear cups didn’t squish them, and they fully cover a kid's ears, meaning there isn’t sound leakage. I was able to wear the iClever headphones for a full workday without noticing them.</p><p>At 9.9 ounces (238 grams), these are some of the heaviest kid headphones I’ve tested. As an adult, I didn’t find the weight uncomfortable, but while 8 or 9 ounces isn’t much for grown-ups, it does add up for your kid. </p><p>My son tried the headphones, and he didn’t find them uncomfortable, but he has complimented lighter headphones like the JBL Junior 320 when we’ve had them before. In comparison, those headphones only weigh 4 ounces, less than half the weight of the Q950.</p><h3 id="connectivity-11">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Wwefhk7E3cbZYoPGTSiiEU" name="iClever-q950--8" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/Wwefhk7E3cbZYoPGTSiiEU.jpg" mos="" align="middle" fullscreen="" width="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 iClever Q950 comes with both wired and Bluetooth connectivity. I’m a big proponent of 3.5mm wired connections when they’re available. These headphones come with a 3.5mm wire that sends the audio pretty cleanly. I can’t tell how they’ll hold up in the long run but for now it seems pretty sturdy. </p><p>Q950 features Bluetooth version 6.0; the most recent version which offers better connection stability and reduced power consumption. This is a huge get for headphones meant for kids. Not every headset has this version, including major releases from this year, like <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">Apple’s AirPods Max 2,</a> which still use version 5.3.</p><p>I was able to cleanly connect to an iPad mini and my Galaxy S26 Ultra with no noticeable dips or drops. That’s all you need, especially if you’re on a road trip and using movies on the tablet to distract.</p><h2 class="article-body__section" id="section-iclever-q950-review-sound-quality"><span>iClever Q950 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NNJwJ7jNMFHYHHwmbggYpU" name="iClever-q950--2" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/NNJwJ7jNMFHYHHwmbggYpU.jpg" mos="" align="middle" fullscreen="" width="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>As mentioned up top, the Q950 headphones might be the best-sounding kids headphones that I’ve tested to date. Now with that praise, if you’re dropping down from a high-end set like the <a href="https://www.tomsguide.com/audio/bowers-and-wilkins-px7-s3-review">Bowers & Wilkins Px7 S3</a> or the <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</a>, then you’ll notice a huge difference.</p><p>But compared to other kid headphones, the Q950 have an open, warm sound. The trebles and mids are clean without losing depth, meaning you should hear what the music is meant to sound like. Conversations in podcasts have a warmth that I found pleasant.</p><p>The bass is surprisingly punchy. No, you won’t mistake these cans for a set of Bowers drivers. But as someone who grew up on a steady diet of Paul Oakenfold passed through terrible headphones and winMX, the Q950 are pretty decent.</p><p>It’s honestly surprising how good these sound. I doubt it’ll turn your kid into an audiophile straight away, but they’ll be able to appreciate what they’re hearing, especially as they start playing their own music.</p><h2 class="article-body__section" id="section-iclever-q950-review-anc"><span>iClever Q950 review: 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="TPXFnDg9oQfP9kHYxndKpU" name="iClever-q950--4" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/TPXFnDg9oQfP9kHYxndKpU.jpg" mos="" align="middle" fullscreen="" width="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>Unfortunately, there is one big flaw you should know. Most kid headphones now feature active noise cancelling, which is marketed as a way to keep your kids from turning the sound up when outside noises get too loud. The Q950 does feature an 80 dB volume cap, which is good. However, I found that the ANC actively made the sound worse, flattening it or making it feel too quiet. It’s a surprising flaw for such a prominent feature.</p><p>As an example, I was listening to a podcast and decided to try out ANC while my partner was in a meeting. Tapping the button suddenly felt like I’d been sucked into an aural void, and I thought I’d accidentally hit a half-mute button I didn’t know existed. Music felt similarly pillowed.</p><p>Normally when I use ANC, like on the JBL Tour One M3, it just deadens outside noise. I don’t think I’ve ever felt the feature fundamentally change the sound of what I was actively hearing. It’s not necessary to use ANC, but if you or your child is bothered by too much outside stimulation, this is a significant drawback to the Q950.</p><h2 class="article-body__section" id="section-iclever-q950-review-what-the-kids-are-saying"><span>iClever Q950 review: What the kids are saying?</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="vB2t2fV58dRcDo2iADywPU" name="iClever-q950--9" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/vB2t2fV58dRcDo2iADywPU.jpg" mos="" align="middle" fullscreen="" width="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 son tried out the Q950 over a couple different sessions with an iPad mini, once on a road trip and once at home. He liked that the headphones were comfortable, though he initially thought they were the iClever Auraas, and at first glance they do look similar. </p><p>He didn’t have much to say about the sound, though he said he liked it. He doesn’t really care about ANC, but he did say he never had to turn up the audio even despite a nagging little sister.</p><h2 class="article-body__section" id="section-iclever-q950-review-verdict"><span>iClever Q950 review: Verdict</span></h2><p>The bar can be pretty low for kid headphones when it comes to audio quality and iClever Q950 clears that bar with flying colors. Honestly, you could buy a set for yourself, and I don’t know that you’d be disappointed. </p><p>The weight and ANC are potential problems, but if your child isn’t bothered by headphone weight and doesn’t use ANC, these are good headphones. Plus, at $69, they won’t break the bank, making them one of the best pairs of kid headphones you can buy. </p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/over-ear-headphones/iclever-q950-review</link>
                                                                            <description>
                            <![CDATA[ The iClever Q950 headphones sound surprisingly good while still offering kid-friendly aesthetics. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">dBPodD3UGcb5WRXYNQL4uN</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/rqxEBNiheZu6pGavEf3xwT-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 03 Aug 2026 06:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Over-Ear Headphones]]></category>
                                                    <category><![CDATA[Audio]]></category>
                                                    <category><![CDATA[Headphones]]></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.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/rqxEBNiheZu6pGavEf3xwT-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[iClever Q950 kid headphones]]></media:description>                                                            <media:text><![CDATA[iClever Q950 kid headphones]]></media:text>
                                <media:title type="plain"><![CDATA[iClever Q950 kid headphones]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/rqxEBNiheZu6pGavEf3xwT-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Headphones that are specifically labeled for children tend to feature a ton of drawbacks, especially when it comes to sound quality. That said, why we recommend specific kid headphones is because most of the ones we test feature volume limiters designed to protect little ears from future hearing damage or loss.</p><p>With that in mind, the iClever Q950 might be the best kid headphones I’ve ever tested. They’re comfortable and sound amazing for kids’ cans. No, they won’t replace the <a href="https://www.tomsguide.com/us/best-headphones,review-1988.html">best headphones</a> for adults, but a kid won’t complain, and even most grown-ups will enjoy listening to this set. Read my full iClever Q950 review to find out why.</p><h2 class="article-body__section" id="section-iclever-q950-review-specs"><span>iClever Q950 review: Specs</span></h2><div ><table><caption>iClever Q950 Specs</caption><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://iclever.com/products/iclever-q950-noise-cancelling-headphones" target="_blank" rel="nofollow">$69</a></p></td></tr><tr><td class="firstcol " ><p><strong>Headphone style</strong></p></td><td  ><p>Over ear</p></td></tr><tr><td class="firstcol " ><p><strong>Drivers</strong></p></td><td  ><p>40mm</p></td></tr><tr><td class="firstcol " ><p><strong>Frequency response</strong></p></td><td  ><p>20Hz - 20kHz</p></td></tr><tr><td class="firstcol " ><p><strong>Safe volume limited</strong></p></td><td  ><p>80dBA</p></td></tr><tr><td class="firstcol " ><p><strong>Battery capacity</strong></p></td><td  ><p>500mAh</p></td></tr><tr><td class="firstcol " ><p><strong>Durability</strong></p></td><td  ><p>No IP rating</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Wired, 3.5mm cable or Bluetooth v6.0, USB-C charging port</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>9.9 oz (238 grams)</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Blue/Purple</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-iclever-q950-review-price-availability"><span>iClever Q950 review: Price & availability</span></h2><p>The iClever Q950 cost <a href="https://www.amazon.com/dp/B0H1BLMC7R" target="_blank" rel="nofollow">$69 at Amazon</a>, but we’ve seen them a bit lower at $59 during sales events. Even at full price, these are pretty good value for money. </p><p>The Q950 competes with other iClever offerings like its colorful <a href="https://www.tomsguide.com/audio/headphones/iclever-auraa-headphones-hands-on">Auraa headphones</a> ($39) and the <a href="https://www.tomsguide.com/audio/headphones/these-stylish-ear-friendly-kid-headphones-promise-to-protect-your-childs-hearing-and-i-legit-agree">Puro Sound Lab BT2200s</a> ($99) or the extremely low-cost $20 <a href="https://www.tomsguide.com/audio/headphones/i-gave-my-kid-the-jbl-junior-320-headphones-they-look-cheap-but-sound-surprisingly-good">JBL Junior 320s</a>. I don’t think you’d go wrong with the other options, especially if you’re trying to save a buck, but personally, I’d recommend the Q950 as the middle ground. Plus, they sound better than the other offerings.</p><h2 class="article-body__section" id="section-iclever-q950-review-design"><span>iClever Q950 review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="EHwkPsehVbnHCFs4H4zcsU" name="iClever-q950--3" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/EHwkPsehVbnHCFs4H4zcsU.jpg" mos="" align="middle" fullscreen="" width="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>Compared to most kid headphones, the iClever Q950 look more sophisticated while also clearly being made for children. </p><p>Compared to the usual grays, blacks and whites that make up adult headphones, the Q950 are quite colorful but not quite as garish as they could be. Largely that has to do with the bright blue or purple hues. The holders have a pleasant metallic sheen. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="BYiYwTxEWr3qHXDLRJe8wT" name="iClever-q950--7" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/BYiYwTxEWr3qHXDLRJe8wT.jpg" mos="" align="middle" fullscreen="" width="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>Outside of colors, these are pretty durable headphones; I performed a couple of drop tests and twisted the headphones around a few times and never felt afraid that I was about to break them, though I’m sure an enterprising tot could destroy them.</p><p>They’re over-ear headphones with extendable ears that fold in, but can also turn out to lay flat if you want. They won’t disappear, but it gives you some flexibility, especially when trying to store them in a backpack. For extra protection, the Q950 does come with a bulky carry case that can hold charging wires and the 3.5mm wire. The case isn’t as bag-friendly, but you can stuff some Pokémon cards in there if you want.</p><h3 id="comfort-10">Comfort</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="S9oHoTU68GAuMZ3wFzmGrU" name="iClever-q950--5" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/S9oHoTU68GAuMZ3wFzmGrU.jpg" mos="" align="middle" fullscreen="" width="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 comfort in mind, one of my personal bugaboos with over-ear headphones is the headband. If I notice the band during a listening session, it becomes grating. I never noticed it with the Q950, which is one of the highest compliments I can pay.</p><p>Despite my adult ears, the ear cups didn’t squish them, and they fully cover a kid's ears, meaning there isn’t sound leakage. I was able to wear the iClever headphones for a full workday without noticing them.</p><p>At 9.9 ounces (238 grams), these are some of the heaviest kid headphones I’ve tested. As an adult, I didn’t find the weight uncomfortable, but while 8 or 9 ounces isn’t much for grown-ups, it does add up for your kid. </p><p>My son tried the headphones, and he didn’t find them uncomfortable, but he has complimented lighter headphones like the JBL Junior 320 when we’ve had them before. In comparison, those headphones only weigh 4 ounces, less than half the weight of the Q950.</p><h3 id="connectivity-11">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Wwefhk7E3cbZYoPGTSiiEU" name="iClever-q950--8" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/Wwefhk7E3cbZYoPGTSiiEU.jpg" mos="" align="middle" fullscreen="" width="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 iClever Q950 comes with both wired and Bluetooth connectivity. I’m a big proponent of 3.5mm wired connections when they’re available. These headphones come with a 3.5mm wire that sends the audio pretty cleanly. I can’t tell how they’ll hold up in the long run but for now it seems pretty sturdy. </p><p>Q950 features Bluetooth version 6.0; the most recent version which offers better connection stability and reduced power consumption. This is a huge get for headphones meant for kids. Not every headset has this version, including major releases from this year, like <a href="https://www.tomsguide.com/audio/headphones/apple-airpods-max-2-review">Apple’s AirPods Max 2,</a> which still use version 5.3.</p><p>I was able to cleanly connect to an iPad mini and my Galaxy S26 Ultra with no noticeable dips or drops. That’s all you need, especially if you’re on a road trip and using movies on the tablet to distract.</p><h2 class="article-body__section" id="section-iclever-q950-review-sound-quality"><span>iClever Q950 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NNJwJ7jNMFHYHHwmbggYpU" name="iClever-q950--2" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/NNJwJ7jNMFHYHHwmbggYpU.jpg" mos="" align="middle" fullscreen="" width="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>As mentioned up top, the Q950 headphones might be the best-sounding kids headphones that I’ve tested to date. Now with that praise, if you’re dropping down from a high-end set like the <a href="https://www.tomsguide.com/audio/bowers-and-wilkins-px7-s3-review">Bowers & Wilkins Px7 S3</a> or the <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</a>, then you’ll notice a huge difference.</p><p>But compared to other kid headphones, the Q950 have an open, warm sound. The trebles and mids are clean without losing depth, meaning you should hear what the music is meant to sound like. Conversations in podcasts have a warmth that I found pleasant.</p><p>The bass is surprisingly punchy. No, you won’t mistake these cans for a set of Bowers drivers. But as someone who grew up on a steady diet of Paul Oakenfold passed through terrible headphones and winMX, the Q950 are pretty decent.</p><p>It’s honestly surprising how good these sound. I doubt it’ll turn your kid into an audiophile straight away, but they’ll be able to appreciate what they’re hearing, especially as they start playing their own music.</p><h2 class="article-body__section" id="section-iclever-q950-review-anc"><span>iClever Q950 review: 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="TPXFnDg9oQfP9kHYxndKpU" name="iClever-q950--4" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/TPXFnDg9oQfP9kHYxndKpU.jpg" mos="" align="middle" fullscreen="" width="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>Unfortunately, there is one big flaw you should know. Most kid headphones now feature active noise cancelling, which is marketed as a way to keep your kids from turning the sound up when outside noises get too loud. The Q950 does feature an 80 dB volume cap, which is good. However, I found that the ANC actively made the sound worse, flattening it or making it feel too quiet. It’s a surprising flaw for such a prominent feature.</p><p>As an example, I was listening to a podcast and decided to try out ANC while my partner was in a meeting. Tapping the button suddenly felt like I’d been sucked into an aural void, and I thought I’d accidentally hit a half-mute button I didn’t know existed. Music felt similarly pillowed.</p><p>Normally when I use ANC, like on the JBL Tour One M3, it just deadens outside noise. I don’t think I’ve ever felt the feature fundamentally change the sound of what I was actively hearing. It’s not necessary to use ANC, but if you or your child is bothered by too much outside stimulation, this is a significant drawback to the Q950.</p><h2 class="article-body__section" id="section-iclever-q950-review-what-the-kids-are-saying"><span>iClever Q950 review: What the kids are saying?</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="vB2t2fV58dRcDo2iADywPU" name="iClever-q950--9" alt="iClever Q950 kid headphones" src="https://cdn.mos.cms.futurecdn.net/vB2t2fV58dRcDo2iADywPU.jpg" mos="" align="middle" fullscreen="" width="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 son tried out the Q950 over a couple different sessions with an iPad mini, once on a road trip and once at home. He liked that the headphones were comfortable, though he initially thought they were the iClever Auraas, and at first glance they do look similar. </p><p>He didn’t have much to say about the sound, though he said he liked it. He doesn’t really care about ANC, but he did say he never had to turn up the audio even despite a nagging little sister.</p><h2 class="article-body__section" id="section-iclever-q950-review-verdict"><span>iClever Q950 review: Verdict</span></h2><p>The bar can be pretty low for kid headphones when it comes to audio quality and iClever Q950 clears that bar with flying colors. Honestly, you could buy a set for yourself, and I don’t know that you’d be disappointed. </p><p>The weight and ANC are potential problems, but if your child isn’t bothered by headphone weight and doesn’t use ANC, these are good headphones. Plus, at $69, they won’t break the bank, making them one of the best pairs of kid headphones you can buy. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I spoke to a hearing expert to find out the best ways to protect your kids' hearing — from headphones to listening limits ]]></title>
                                                                                                <dc:content><![CDATA[ <p>For some of us, the road trips of summer are starting to wind down and back-to-school needs are beginning to creep up. The luxury of headphones in the car becomes a necessity as kids commute to school or use them to focus on schoolwork.</p><p>You may be inclined to hand your kids a set of headphones you no longer use, or you’ve spotted some cheapo ones at your local drugstore. But those cans may be doing more harm than you know, as the lack of volume control can lead to permanent hearing loss.</p><p>Fortunately, there are options that can be safer for your kids. With more kids wearing headphones that seem to be glued to their heads, how can you protect their hearing now and in the future?</p><p>We spoke to a hearing expert and the makers of kid-friendly headphones to find out how volume-limiting headphones can assist parents in protecting their children’s hearing and tips for doing more beyond buying appropriate headphones. </p><h2 id="we-only-get-one-set-of-ears">“We only get one set of ears”</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5277px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="ZuhArCGCz9QYjYvWBjV4CK" name="Noise.jpg" alt="A woman holding her hands to her ears to block out the noise" src="https://cdn.mos.cms.futurecdn.net/ZuhArCGCz9QYjYvWBjV4CK.jpg" mos="" align="middle" fullscreen="" width="5277" height="2968" 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>The world in general is a loud place with a multitude of sounds that over time can cause noise-induced hearing loss. Both OSHA and the World Health Organization <a href="https://www.osha.gov/noise" target="_blank"><u>recommend 85 decibels</u></a> as a safe listening level for people in industrial settings. That rating is over an eight-hour “time-weighted average.” </p><p>Per the <a href="https://www.audiology.org/wp-content/uploads/2023/09/PR23-Poster-Noise_Chart-8.5x11-min.png" target="_blank"><u>American Academy of Audiology</u></a>, anything over that level can cause permanent hearing loss, and <a href="https://www.tomsguide.com/features/i-used-airpods-pro-instead-of-ear-plugs-at-a-concert-heres-what-happened">hearing protection</a> is recommended: “regardless of how long the sound lasts.” At 80 dB, the sound is equivalent to an alarm clock, while sounds in the 90 to 110 dB range include concerts, power tools and earbuds (at full volume).</p><p>Kaitlyin Kennedy, an audiologist and member of the Academy, told Tom’s Guide that the recommendation for children is more conservative, at 75 dB and 80 dB for adults. She noted that headphone use in teens has gotten worse in general in the last few years.</p><p>A <a href="https://www.michiganmedicine.org/health-lab/are-headphones-and-earbuds-exposing-your-children-noise-health-risks" target="_blank">study by the University of Michigan</a> in 2024 found more than half of kids age 5 -12 have their own personal audio devices and use them at least an hour a day.</p><p>“When hearing loss sets in, whether genetic or noise exposure, it grows over time,” Kennedy said. “It’s a snowball; one instance won’t [cause problems], multiple will.”</p><p>She added that while tech is improving, including with <a href="https://www.tomsguide.com/phones/android-phones/how-to-enable-sound-amplifier-on-google-pixel">hearing aids</a>, nothing is the same as natural hearing. </p><h2 id="what-headphone-manufacturers-offer">What headphone manufacturers offer</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LGsQ8idkj8WQE6BCdCc76h" name="Puro-Sound-BT2200+--1-LIST" alt="Puro Sound Labs BT2200 Plus kid-friendly headphones" src="https://cdn.mos.cms.futurecdn.net/LGsQ8idkj8WQE6BCdCc76h.jpg" mos="" align="middle" fullscreen="" width="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>Fortunately, headphones can be capped so that the volume of audio coming through the drivers can’t exceed a set decibel level. </p><p>Most of the kids-friendly headphones Tom’s Guide has tested so far do feature volume caps around 85 dB. <a href="https://www.tomsguide.com/audio/headphones/iclever-auraa-headphones-hands-on">iClever</a> limits volume at 80 dB for many of its headphones based on the EU EN50332-1 standard and WHO recommendations. </p><div class="product"><a data-dimension112="8d989e40-8c53-11f1-9172-01af64fc74f3" data-action="Deal Block" data-label="Q950" data-dimension48="Q950" data-dimension25="$69" href="https://www.amazon.com/dp/B0H1BLMC7R?th=1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1584px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="wHREy2uHf7fhdtgK7bev4M" name="q950ima" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/wHREy2uHf7fhdtgK7bev4M.jpg" mos="" align="middle" fullscreen="" width="1584" height="1584" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>iClever’s Q950 are the cleanest-sounding kid headphones that I’ve ever tested. That comes despite amazingly poor ANC and a battery that seems to run even in wired mode. Featuring the important volume limiter and comfortable ear cups, these are headphones that even adults might want to wear.<a class="view-deal button" href="https://www.amazon.com/dp/B0H1BLMC7R?th=1" target="_blank" rel="nofollow" data-dimension112="8d989e40-8c53-11f1-9172-01af64fc74f3" data-action="Deal Block" data-label="Q950" data-dimension48="Q950" data-dimension25="$69">View Deal</a></p></div><p>iClever also uses its “SafeSound” technology, which combines active noise canceling with maximum volume to ensure kids aren’t trying to turn the volume all the way up to hear music or media over outside noise.</p><p><a href="https://www.tomsguide.com/audio/headphones/these-stylish-ear-friendly-kid-headphones-promise-to-protect-your-childs-hearing-and-i-legit-agree">Puro Sound Labs</a>, based out of Southern California, purposely caps all of its headphones at 85dB. Founder Dave Russell set out to create safer headphones after his own daughter suffered noise-induced hearing loss that an audiologist blamed on constant headphone use.</p><p>“One in five teens are at risk of some level of noise-induced hearing loss due to headphones,” VP of sales Davis Camarigg told Tom’s Guide. He noted that even the company’s adult and older-teen-targeted headphones only go up to 95dB, though the company doesn’t just cap the volume or use ANC without also ensuring the audio still sounds good. </p><div class="product"><a data-dimension112="8d989f08-8c53-11f1-bd3a-1555038a0d4f" data-action="Deal Block" data-label="BT2200+" data-dimension48="BT2200+" data-dimension25="$99" href="https://www.amazon.com/dp/B0C3T39C58?th=1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="vxQ4vFLoEZo7XhTbMnPXaL" name="Puro-Sound-Labs-BT2200-Plus-03" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/vxQ4vFLoEZo7XhTbMnPXaL.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0C3T39C58?th=1" target="_blank" rel="nofollow" data-dimension112="8d989f08-8c53-11f1-bd3a-1555038a0d4f" data-action="Deal Block" data-label="BT2200+" data-dimension48="BT2200+" data-dimension25="$99">BT2200+: $99</a></strong><br>Puro Labs offers both premium aesthetics and ear safety with the volume cap. These headphones are durable and can stand up to rowdy kids and more careless teenagers. At $99, you want something that can hold up to the rigors of kid life, and these do.<a class="view-deal button" href="https://www.amazon.com/dp/B0C3T39C58?th=1" target="_blank" rel="nofollow" data-dimension112="8d989f08-8c53-11f1-bd3a-1555038a0d4f" data-action="Deal Block" data-label="BT2200+" data-dimension48="BT2200+" data-dimension25="$99">View Deal</a></p></div><h2 id="if-it-s-so-loud-they-can-t-hear-you-speak-it-s-too-loud">“If it's so loud they can't hear you speak, it's too loud”</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="qBATFf8rWbKGrw2V6TpgEW" name="iClever-Auraa-14" alt="iClever Auraa kid headphones" src="https://cdn.mos.cms.futurecdn.net/qBATFf8rWbKGrw2V6TpgEW.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Buying the correct headphones and limiting how loud the audio you listen to is an important aspect of protecting your and your children’s hearing. But there are other ways to protect yourself. </p><p>Kennedy provided several tips to help protect your ears.</p><p>She recommended that everyone reduce the amount of time they spend listening to audio, especially when using headphones. “Keep [settings] on 60% for no more than 60 minutes and then take a listening break,” Kennedy said. </p><p>Kennedy said that by the time kids hit teenage years, their ears are much more adult, but damage can be done when they’re younger because the sounds are louder to developing ears. </p><h2 id="your-phone-can-help">Your phone can help</h2><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="cG9Hff38XFHCP7Exzjf2tL" name="iPhone-headphone-level-hearing-LIST2.jpg" alt="Screenshots of an iPhone with the Hearing Accessibility app in use." src="https://cdn.mos.cms.futurecdn.net/cG9Hff38XFHCP7Exzjf2tL.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: Credit: Shutterstock/Tom’s Guide)</span></figcaption></figure><p>With many kids on smartphones, you can set limits or monitoring systems. For example, with headphones connected, you can go to the Control Center on your iPhone and see your headphones’ dB levels. Under Accessibility Shortcuts, you can also limit loud sounds in your headphones under <strong>Settings ></strong> <strong>Sound & Haptics ></strong> <strong>Headphone Safety,</strong> then <strong>Reduce Loud Sounds.</strong> </p><p>Android has a “Volume monitor” that will tell you how long you’ve spent listening to loud volumes in your headphones. You can find it under Digital Wellbeing. There’s also a media volume limit feature which lets you cap the volume between 85dB and 100dB.</p><p>In all, protecting your child's hearing takes some proactive approaches on your part, from acquiring safe equipment to actively setting up systems via your phone or the electronic devices you let your kid use.</p><p>Fortunately, it's quite doable, and there are plenty of options to help you make the right choice.</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/wellness/sleep-tech/6-apple-watch-settings-you-should-change-now-for-more-accurate-sleep-tracking">6 Apple Watch settings you should change now for more accurate sleep tracking</a></li><li><a href="https://www.tomsguide.com/computing/macbooks/ive-been-using-macbooks-for-10-years-heres-15-must-have-accessories-id-buy-for-an-instant-upgrade">I've been using MacBooks for 10 years — here's 15 must-have accessories I'd buy for an instant upgrade</a></li><li><a href="https://www.tomsguide.com/phones/iphones/after-using-the-iphone-16e-for-about-a-year-the-iphone-17e-shouldnt-launch-without-these-3-upgrades">I've used the iPhone 16e for a year — and the iPhone 17e shouldn’t launch without these 3 upgrades</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/audio/headphones/i-spoke-to-a-hearing-expert-to-find-out-the-best-ways-to-protect-your-kids-hearing-from-headphones-to-listening-limits</link>
                                                                            <description>
                            <![CDATA[ Protecting your little one's hearing takes everything from kid-friendly headphones to appropriate settings. An expert told Tom's Guide how. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">JedxFgcpWdeStbYDmqo5DT</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/BSyVcCPUHbeUwDeM2s3WPC-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 02 Aug 2026 12:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Headphones]]></category>
                                                    <category><![CDATA[Audio]]></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.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/BSyVcCPUHbeUwDeM2s3WPC-1280-80.jpg">
                                                            <media:credit><![CDATA[Ute Grabowsky/Photothek/Getty]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Small child wearing protective headphones]]></media:description>                                                            <media:text><![CDATA[Small child wearing protective headphones]]></media:text>
                                <media:title type="plain"><![CDATA[Small child wearing protective headphones]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/BSyVcCPUHbeUwDeM2s3WPC-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>For some of us, the road trips of summer are starting to wind down and back-to-school needs are beginning to creep up. The luxury of headphones in the car becomes a necessity as kids commute to school or use them to focus on schoolwork.</p><p>You may be inclined to hand your kids a set of headphones you no longer use, or you’ve spotted some cheapo ones at your local drugstore. But those cans may be doing more harm than you know, as the lack of volume control can lead to permanent hearing loss.</p><p>Fortunately, there are options that can be safer for your kids. With more kids wearing headphones that seem to be glued to their heads, how can you protect their hearing now and in the future?</p><p>We spoke to a hearing expert and the makers of kid-friendly headphones to find out how volume-limiting headphones can assist parents in protecting their children’s hearing and tips for doing more beyond buying appropriate headphones. </p><h2 id="we-only-get-one-set-of-ears">“We only get one set of ears”</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5277px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="ZuhArCGCz9QYjYvWBjV4CK" name="Noise.jpg" alt="A woman holding her hands to her ears to block out the noise" src="https://cdn.mos.cms.futurecdn.net/ZuhArCGCz9QYjYvWBjV4CK.jpg" mos="" align="middle" fullscreen="" width="5277" height="2968" 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>The world in general is a loud place with a multitude of sounds that over time can cause noise-induced hearing loss. Both OSHA and the World Health Organization <a href="https://www.osha.gov/noise" target="_blank"><u>recommend 85 decibels</u></a> as a safe listening level for people in industrial settings. That rating is over an eight-hour “time-weighted average.” </p><p>Per the <a href="https://www.audiology.org/wp-content/uploads/2023/09/PR23-Poster-Noise_Chart-8.5x11-min.png" target="_blank"><u>American Academy of Audiology</u></a>, anything over that level can cause permanent hearing loss, and <a href="https://www.tomsguide.com/features/i-used-airpods-pro-instead-of-ear-plugs-at-a-concert-heres-what-happened">hearing protection</a> is recommended: “regardless of how long the sound lasts.” At 80 dB, the sound is equivalent to an alarm clock, while sounds in the 90 to 110 dB range include concerts, power tools and earbuds (at full volume).</p><p>Kaitlyin Kennedy, an audiologist and member of the Academy, told Tom’s Guide that the recommendation for children is more conservative, at 75 dB and 80 dB for adults. She noted that headphone use in teens has gotten worse in general in the last few years.</p><p>A <a href="https://www.michiganmedicine.org/health-lab/are-headphones-and-earbuds-exposing-your-children-noise-health-risks" target="_blank">study by the University of Michigan</a> in 2024 found more than half of kids age 5 -12 have their own personal audio devices and use them at least an hour a day.</p><p>“When hearing loss sets in, whether genetic or noise exposure, it grows over time,” Kennedy said. “It’s a snowball; one instance won’t [cause problems], multiple will.”</p><p>She added that while tech is improving, including with <a href="https://www.tomsguide.com/phones/android-phones/how-to-enable-sound-amplifier-on-google-pixel">hearing aids</a>, nothing is the same as natural hearing. </p><h2 id="what-headphone-manufacturers-offer">What headphone manufacturers offer</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LGsQ8idkj8WQE6BCdCc76h" name="Puro-Sound-BT2200+--1-LIST" alt="Puro Sound Labs BT2200 Plus kid-friendly headphones" src="https://cdn.mos.cms.futurecdn.net/LGsQ8idkj8WQE6BCdCc76h.jpg" mos="" align="middle" fullscreen="" width="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>Fortunately, headphones can be capped so that the volume of audio coming through the drivers can’t exceed a set decibel level. </p><p>Most of the kids-friendly headphones Tom’s Guide has tested so far do feature volume caps around 85 dB. <a href="https://www.tomsguide.com/audio/headphones/iclever-auraa-headphones-hands-on">iClever</a> limits volume at 80 dB for many of its headphones based on the EU EN50332-1 standard and WHO recommendations. </p><div class="product"><a data-dimension112="8d989e40-8c53-11f1-9172-01af64fc74f3" data-action="Deal Block" data-label="Q950" data-dimension48="Q950" data-dimension25="$69" href="https://www.amazon.com/dp/B0H1BLMC7R?th=1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1584px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="wHREy2uHf7fhdtgK7bev4M" name="q950ima" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/wHREy2uHf7fhdtgK7bev4M.jpg" mos="" align="middle" fullscreen="" width="1584" height="1584" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>iClever’s Q950 are the cleanest-sounding kid headphones that I’ve ever tested. That comes despite amazingly poor ANC and a battery that seems to run even in wired mode. Featuring the important volume limiter and comfortable ear cups, these are headphones that even adults might want to wear.<a class="view-deal button" href="https://www.amazon.com/dp/B0H1BLMC7R?th=1" target="_blank" rel="nofollow" data-dimension112="8d989e40-8c53-11f1-9172-01af64fc74f3" data-action="Deal Block" data-label="Q950" data-dimension48="Q950" data-dimension25="$69">View Deal</a></p></div><p>iClever also uses its “SafeSound” technology, which combines active noise canceling with maximum volume to ensure kids aren’t trying to turn the volume all the way up to hear music or media over outside noise.</p><p><a href="https://www.tomsguide.com/audio/headphones/these-stylish-ear-friendly-kid-headphones-promise-to-protect-your-childs-hearing-and-i-legit-agree">Puro Sound Labs</a>, based out of Southern California, purposely caps all of its headphones at 85dB. Founder Dave Russell set out to create safer headphones after his own daughter suffered noise-induced hearing loss that an audiologist blamed on constant headphone use.</p><p>“One in five teens are at risk of some level of noise-induced hearing loss due to headphones,” VP of sales Davis Camarigg told Tom’s Guide. He noted that even the company’s adult and older-teen-targeted headphones only go up to 95dB, though the company doesn’t just cap the volume or use ANC without also ensuring the audio still sounds good. </p><div class="product"><a data-dimension112="8d989f08-8c53-11f1-bd3a-1555038a0d4f" data-action="Deal Block" data-label="BT2200+" data-dimension48="BT2200+" data-dimension25="$99" href="https://www.amazon.com/dp/B0C3T39C58?th=1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><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="vxQ4vFLoEZo7XhTbMnPXaL" name="Puro-Sound-Labs-BT2200-Plus-03" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/vxQ4vFLoEZo7XhTbMnPXaL.jpg" mos="" align="middle" fullscreen="" width="1600" height="900" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><strong><a href="https://www.amazon.com/dp/B0C3T39C58?th=1" target="_blank" rel="nofollow" data-dimension112="8d989f08-8c53-11f1-bd3a-1555038a0d4f" data-action="Deal Block" data-label="BT2200+" data-dimension48="BT2200+" data-dimension25="$99">BT2200+: $99</a></strong><br>Puro Labs offers both premium aesthetics and ear safety with the volume cap. These headphones are durable and can stand up to rowdy kids and more careless teenagers. At $99, you want something that can hold up to the rigors of kid life, and these do.<a class="view-deal button" href="https://www.amazon.com/dp/B0C3T39C58?th=1" target="_blank" rel="nofollow" data-dimension112="8d989f08-8c53-11f1-bd3a-1555038a0d4f" data-action="Deal Block" data-label="BT2200+" data-dimension48="BT2200+" data-dimension25="$99">View Deal</a></p></div><h2 id="if-it-s-so-loud-they-can-t-hear-you-speak-it-s-too-loud">“If it's so loud they can't hear you speak, it's too loud”</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="qBATFf8rWbKGrw2V6TpgEW" name="iClever-Auraa-14" alt="iClever Auraa kid headphones" src="https://cdn.mos.cms.futurecdn.net/qBATFf8rWbKGrw2V6TpgEW.jpg" mos="" align="middle" fullscreen="" width="2000" height="1126" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>Buying the correct headphones and limiting how loud the audio you listen to is an important aspect of protecting your and your children’s hearing. But there are other ways to protect yourself. </p><p>Kennedy provided several tips to help protect your ears.</p><p>She recommended that everyone reduce the amount of time they spend listening to audio, especially when using headphones. “Keep [settings] on 60% for no more than 60 minutes and then take a listening break,” Kennedy said. </p><p>Kennedy said that by the time kids hit teenage years, their ears are much more adult, but damage can be done when they’re younger because the sounds are louder to developing ears. </p><h2 id="your-phone-can-help">Your phone can help</h2><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="cG9Hff38XFHCP7Exzjf2tL" name="iPhone-headphone-level-hearing-LIST2.jpg" alt="Screenshots of an iPhone with the Hearing Accessibility app in use." src="https://cdn.mos.cms.futurecdn.net/cG9Hff38XFHCP7Exzjf2tL.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: Credit: Shutterstock/Tom’s Guide)</span></figcaption></figure><p>With many kids on smartphones, you can set limits or monitoring systems. For example, with headphones connected, you can go to the Control Center on your iPhone and see your headphones’ dB levels. Under Accessibility Shortcuts, you can also limit loud sounds in your headphones under <strong>Settings ></strong> <strong>Sound & Haptics ></strong> <strong>Headphone Safety,</strong> then <strong>Reduce Loud Sounds.</strong> </p><p>Android has a “Volume monitor” that will tell you how long you’ve spent listening to loud volumes in your headphones. You can find it under Digital Wellbeing. There’s also a media volume limit feature which lets you cap the volume between 85dB and 100dB.</p><p>In all, protecting your child's hearing takes some proactive approaches on your part, from acquiring safe equipment to actively setting up systems via your phone or the electronic devices you let your kid use.</p><p>Fortunately, it's quite doable, and there are plenty of options to help you make the right choice.</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/wellness/sleep-tech/6-apple-watch-settings-you-should-change-now-for-more-accurate-sleep-tracking">6 Apple Watch settings you should change now for more accurate sleep tracking</a></li><li><a href="https://www.tomsguide.com/computing/macbooks/ive-been-using-macbooks-for-10-years-heres-15-must-have-accessories-id-buy-for-an-instant-upgrade">I've been using MacBooks for 10 years — here's 15 must-have accessories I'd buy for an instant upgrade</a></li><li><a href="https://www.tomsguide.com/phones/iphones/after-using-the-iphone-16e-for-about-a-year-the-iphone-17e-shouldnt-launch-without-these-3-upgrades">I've used the iPhone 16e for a year — and the iPhone 17e shouldn’t launch without these 3 upgrades</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
            </channel>
</rss>