<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="https://purl.org/dc/elements/1.1/"
     xmlns:dcterms="http://purl.org/dc/terms/"
     xmlns:media="http://search.yahoo.com/mrss/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:cf="https://www.futureplc.com/rss/content-flags"
>
    <channel>
                    <atom:link href="https://www.tomsguide.com/feeds/tag/cameras-photography" rel="self" type="application/rss+xml" />
                            <title><![CDATA[ Latest from Tom's Guide in Cameras-photography ]]></title>
                <link>https://www.tomsguide.com/cameras-photography</link>
        <description><![CDATA[ All the latest cameras-photography content from the Tom's Guide team ]]></description>
                                    <lastBuildDate>Fri, 24 Jul 2026 18:00:00 +0000</lastBuildDate>
                            <language>en</language>
                                <item>
                                                            <title><![CDATA[ Perseid meteor shower 2026: The best cameras and telescopes to capture the action ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/perseid-meteor-shower-2026-the-best-cameras-and-telescopes-to-capture-the-action</link>
                                                                            <description>
                            <![CDATA[ The Perseids meteor shower is visible now and will until late August. If you want to take a closer look, or even photograph it, then you'll need some specialized equipment — which is where this list of cameras and telescopes comes in handy. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">3b5r5EA2YgHUyWoWi6bza8</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/QrYB7TuPDEUX74bxr8b2CN-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 24 Jul 2026 18:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></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/QrYB7TuPDEUX74bxr8b2CN-1280-80.jpg">
                                                            <media:credit><![CDATA[Celestron / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of telescopes and cameras on a pale background.]]></media:description>                                                            <media:text><![CDATA[A selection of telescopes and cameras on a pale background.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of telescopes and cameras on a pale background.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/QrYB7TuPDEUX74bxr8b2CN-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Perseids meteor shower is a beautiful event that lights up the night sky around about this time each year. It's been possible to see the meteors for a few days now and they will continue until late August, so if you haven't looked for them yet, there's still time.</p><p>It's possible to see the Perseids with your own eyes, of course, but to really make the most out of the occasion, or if you want to take some good quality photographs, then you'll want to invest in some specialist equipment. </p><p>For this article I've picked out 12 great items that will get you a better view of this spectacular event, from cameras to telescopes, tripods and other essential astrophotography kit. Best of all, some of them have big savings right now at Amazon. Happy skywatching!</p>        <div class="featured_product_block featured_block_hero" data-id="bd742638-8754-11f1-b3e0-1d8c73e3ea34">            <a href="https://www.amazon.com/Hoya-Starscape-Light-Pollution-Camera-Filter/dp/B082XM39SN/" data-model-name="Hoya 52mm Starscape Light Pollution Cut Filter" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:112.36%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Fvn6iuwCavpWUGvpFTnCJj.jpg" alt="Hoya 52mm Starscape Light Pollution Cut Filter, Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Hoya</div>                                        <div class="featured__title">Hoya 52mm Starscape Light Pollution Cut Filter</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd7426b0-8754-11f1-b93d-59109d581b45">            <a href="https://www.amazon.com/Celestron-22030-Portable-Telescope-Smartphone/dp/B07J15P2N3/" data-model-name="Celestron Travel Scope 80mm Portable Refractor Telescope" 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/XAq55dRkDts73SwbKoqQRj.jpg" alt="Celestron Travel Scope 80 Portable Refractor Telescope – 80mm Aperture, Fully-Coated Glass Optics – Includes Tripod, Smartphone Adapter, Backpack & Software – Ideal for Beginners & Travel"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Celestron</div>                                        <div class="featured__title">Celestron Travel Scope 80mm Portable Refractor Telescope</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd742728-8754-11f1-a5b9-83f732139dbe">            <a href="https://www.amazon.com/263AB-CNC-Machined-Arca-Compatible-Multi-Angle-Photography/dp/B0FY3T8GHW/" data-model-name="Vanguard Alta Pro 2+ Camera Tripod" 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/uvEKzLXKT6uKMFbUWyP8kj.jpg" alt="Vanguard Alta Pro 2+ 263ab G2 Camera Tripod With Cnc-Machined Arca-Compatible Bh-60 Ball Head and Multi-Angle Center Column for Horizonal, Overhead and Low-Angle Photography"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>VANGUARD</div>                                        <div class="featured__title">Vanguard Alta Pro 2+ Camera Tripod</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="ce2e24d8-8772-11f1-bc48-b1308b5abde6">            <a href="https://www.amazon.com/Professional-Auto-focus-Electronic-Telescopes-Astrophotography/dp/B0GDXNS3GG/" data-model-name="Fashionari Smart Telescope with 4K Astrophotography Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rev5Sx4eV93rHNcxDBdNwM.jpg" alt="Smart Telescope, Professional Refractor Auto-Focus Portable Digital Electronic Telescopes With 4k Astrophotography Camera, Internal and External Dual Screens Spotter for Adults and Beginners Astronomy"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>FASHIONARI</div>                                        <div class="featured__title">Fashionari Smart Telescope with 4K Astrophotography Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd742796-8754-11f1-a08b-9feff35d6b8b">            <a href="https://www.amazon.com/Celestron-Refractor-Smartphone-Refracting-22403/dp/B01L0EQPLC/" data-model-name="Celestron Inspire 100az Refractor Telescope with Smartphone Adapter" 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/V94viaKL3RsPrc8j8nVt5k.jpg" alt="Celestron Inspire 100az Refractor Telescope With Built-In Smartphone Adapter, Blue"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Celestron</div>                                        <div class="featured__title">Celestron Inspire 100az Refractor Telescope with Smartphone Adapter</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd74287c-8754-11f1-818a-632466ae0991">            <a href="https://www.amazon.com/Celestron-StarSense-Smartphone-App-Enabled-Compatible/dp/B083JRF1MH/" data-model-name="Celestron Starsense Explorer Dx 130az App-Enabled Telescope" 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/Rn7R7c4WRkMK67LYnwzAgk.jpg" alt="Celestron Starsense Explorer Dx 130az App-Enabled Telescope – 130mm Newtonian Reflector With Smartphone Dock & Starsense App – Iphone & Android Compatible – Easy-To-Use for Beginners"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Celestron</div>                                        <div class="featured__title">Celestron Starsense Explorer Dx 130az App-Enabled Telescope</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd7428f4-8754-11f1-a099-2bfebf7991a0">            <a href="https://www.amazon.com/Celestron-NexStar-4-SE-Telescope/dp/B000GUFOBO/" data-model-name="Celestron NexStar 4SE Computerized Telescope" 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/z32uobUXMvfmskWE4fDzDh.jpg" alt="Celestron NexStar 4SE Computerized Telescope"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Celestron</div>                                        <div class="featured__title">Celestron NexStar 4SE Computerized Telescope</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd742962-8754-11f1-afad-89aaeec27a21">            <a href="https://www.amazon.com/ZWO-Seestar-S30-Pro-Astrophotography/dp/B0GYX9GSVX/" data-model-name="Zwo Seestar S30 Pro Smart Telescope" 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/SzNysgDXjQB8hqHP4ZiSRm.jpg" alt="Zwo Seestar S30 Pro Smart Telescope, App-Controlled Astrophotography"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>ZWO</div>                                        <div class="featured__title">Zwo Seestar S30 Pro Smart Telescope</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd7429da-8754-11f1-a1ff-0b5722ed77e6">            <a href="https://www.amazon.com/Celestron-NexStar-6-SE-Telescope/dp/B000GUKTDM/ref=sr_1_1?crid=3R51IMH1ZUQDP&dib=eyJ2IjoiMSJ9.bV8TYnDLHPkZPxa6_BwKoZvwtB_C1G9xU7ygUyEpcYwewnkzCS3IJKKiPH9HmmwGHL13mnKbqwRnOOYlNIIqm6fA05elY4kWc8wrhdjW4yEcsPphVQhd3U7fP-WIh-uvWTkGLSMsiBkU3LLHzs6Ygx2sA2fcIH94F3fLu6rAGIOmAeQQBvk5HR_fij7A4v8FPoh-M4I0ERUVF1cVFHiTrpDTuASOHi2kdlc_h2D9NH0.ovTSA3zyaqjBbm9RJvGxBAWrMtlubtFQwWbiPYZ1B90&dib_tag=se&keywords=Celestron+11068+NexStar+6SE+Computerised+Schmidt-Cassegrain+Telescope&qid=1784908692&sprefix=celestron+11068+nexstar+6se+computerised+schmidt-cassegrain+telescope%2Caps%2C197&sr=8-1" data-model-name="Celestron Nexstar 6SE Computerised Telescope" 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/zxoS9rNcwDJfXJLUXsoCem.jpg" alt="Celestron 11068 Nexstar 6se Computerised Schmidt-Cassegrain Telescope With Fully Automated Mount, Skyalign Technology and Xlt Coating, Black/orange"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Celestron</div>                                        <div class="featured__title">Celestron Nexstar 6SE Computerised Telescope</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd742a48-8754-11f1-8fb8-d92da41d0f22">            <a href="https://www.amazon.com/Canon-EOS-Mark-Mirrorless-Oversampling/dp/B0BL7ZVY78/ref=sr_1_2?crid=3K1OC93Y5BFH3&dib=eyJ2IjoiMSJ9.-sLA5UdkF_1J_rsuEQz9gQFqX9gTG8gSkFqxVrKvCUNkUnJ-0xF5GE_J5edT2451cyvfAou3LwfL9d5MVS8BHqqqvETL93inpkjUlZa5SAq6wzbo5-nJsoWghMoWpsLXO1PFANpQvkLSXc9o3lhW7_YRXYMAKdzN-t3pWTqBbiKnsAfExqHf2Cbt9Zp2drdNzeQrbQUDrEhNiSlWPfTDLjhnbrMrM0-RGXn-ttEsMUY.rWNPsj7w1TDJNTmdzX-X1XG1qv7IV_n1rtHRYEyVbV4&dib_tag=se&keywords=Canon%2BEOS%2BR6%2BMark%2BII%2BFull%2BFrame%2BMirrorless%2BCamera%2BBody&nsdOptOutParam=true&qid=1784908831&sprefix=canon%2Beos%2Br6%2Bmark%2Bii%2Bfull%2Bframe%2Bmirrorless%2Bcamera%2Bbody%2Caps%2C177&sr=8-2&th=1" data-model-name="Canon EOS R6 Mark II Full Frame Mirrorless Camera Body" 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/UV6vUnenJc9EGB9N66ajsm.jpg" alt="Canon Eos R6 Mark Ii Full Frame Mirrorless Camera Body Only - Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Canon EOS R6 Mark II Full Frame Mirrorless Camera Body</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd742ab6-8754-11f1-8781-1fd45134182d">            <a href="https://www.amazon.com/Sony-a7R-Mirrorless-Camera-Interchangeable/dp/B076TGDHPT/" data-model-name="Sony A7r III Mirrorless Camera: 42.4mp Full Frame High Resolution Interchangeable Lens Digital Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/YbMMbp9S9zZoQfG28GMDKn.jpg" alt="Sony A7r Iii Mirrorless Camera: 42.4mp Full Frame High Resolution Interchangeable Lens Digital Camera With Front End Lsi Image Processor, 4k Hdr Video and 3" Lcd Screen - Ilce7rm3/b Body, Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony A7r III Mirrorless Camera: 42.4mp Full Frame High Resolution Interchangeable Lens Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="bd742b2e-8754-11f1-add1-ef80da22d0b4">            <a href="https://www.amazon.com/OM-SYSTEM-Mirrorless-M-Zuiko-Digital/dp/B0DVHMVSLW/" data-model-name="OM System Olympus OM-3 Silver Micro Four Thirds System Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:65.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/fJcugpgnEdwqNQ5CsznNYn.jpg" alt="Om System Olympus Om-3 Silver Micro Four Thirds System Camera M.zuiko Digital Ed 12-45mm F4.0 Pro Kit Travel Camera Weather Sealed Design 5-Axis Image Stabilization"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>OM SYSTEM</div>                                        <div class="featured__title">OM System Olympus OM-3 Silver Micro Four Thirds System Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Every good content creator needs a ring light, and this cordless one will have your selfies looking sharp ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/lume-cube-cordless-ring-light-pro</link>
                                                                            <description>
                            <![CDATA[ The Lume Cube Cordless Ring Light Pro is a little expensive but a good choice for those who need portability and lots of adjustment. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">NvCHh2hbLDGmPyUzpwTX4E</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/Zs7PBfdj2YcvHWJCaarQB8-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 24 Jul 2026 12:47:13 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></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/Zs7PBfdj2YcvHWJCaarQB8-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Lume Cube Cordless Ring Light Pro and selfie using the ring light]]></media:description>                                                            <media:text><![CDATA[Lume Cube Cordless Ring Light Pro and selfie using the ring light]]></media:text>
                                <media:title type="plain"><![CDATA[Lume Cube Cordless Ring Light Pro and selfie using the ring light]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/Zs7PBfdj2YcvHWJCaarQB8-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Adding one of the <a href="https://www.tomsguide.com/best-picks/best-ring-lights">best ring lights</a> to your setup is the easiest way to level up your content game. There are so many options to choose from. While I think the Lume Cube Cordless Ring Light Pro is a decent option for amateur content creators, it’s worth bearing in mind that there are cheaper options out there with better maximum illumination settings. </p><p>But with that said, if you can drop $300 on a light, the conveniently portable build is ideal for anyone who needs to swiftly move locations, and the highly adjustable brightness, temperature and height can suit most eventualities. It’s great to have remote control over the settings, and having the choice between battery-powered or wired operation opens up a lot of doors. </p><p>To find out more about my experience using the light, you can keep reading this full Lume Cube Cordless Ring Light Pro review.</p><h2 class="article-body__section" id="section-lume-cube-cordless-ring-light-pro-specs"><span>Lume Cube Cordless Ring Light Pro: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.amazon.com/Professional-Photography-Streaming-Adjustable-Brightness/dp/B0BTMX8WQ7" target="_blank" rel="nofollow">$299</a></p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>18 x 70 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>8.1 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Power cord length</strong></p></td><td  ><p>6.5 feet</p></td></tr><tr><td class="firstcol " ><p><strong>Number of LEDs</strong></p></td><td  ><p>256</p></td></tr><tr><td class="firstcol " ><p><strong>Maximum illumination</strong></p></td><td  ><p>1700 lux/0.5m</p></td></tr><tr><td class="firstcol " ><p><strong>CRI (color rendering index)</strong></p></td><td  ><p>96+</p></td></tr><tr><td class="firstcol " ><p><strong>Color temperature</strong></p></td><td  ><p>2700K - 7500K</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-lume-cube-cordless-ring-light-pro-price-availability"><span>Lume Cube Cordless Ring Light Pro: Price & availability</span></h2><p>The Lume Cube Cordless Ring Light Pro is available to purchase directly from the <a href="https://lumecube.com/products/ring-light-pro" target="_blank" rel="nofollow">Lume Cube online store</a> or from <a href="https://www.amazon.com/Professional-Photography-Streaming-Adjustable-Brightness/dp/B0BTMX8WQ7" target="_blank" rel="nofollow">Amazon for $299</a>. The ring light only comes in black or white, and while both colors look good, I think black has the more professional appearance. </p><p>While there’s a lot to love about the Lume Cube ring light, the price is not one of those things. At $300, it’s an investment, but not one you need to make if you’re looking for a light for content creation — other options are just as good for a fraction of the price. </p><p>For example, the <a href="https://www.tomsguide.com/cameras-photography/godox-lr120-review">Godox LR120</a> 18-inch ring light is just $59 and still offers plenty of temperature and brightness adjustments. There’s also a built-in phone holder with this light, but the tripod feels a little unstable compared to the Lume Cube. My top choice for a larger ring light is the <a href="https://www.tomsguide.com/cameras-photography/neewer-rp19h-review">Neewer RP19H</a>, a 19-inch ring light for $149. The Neewer light is far brighter than the Lume Cube and has much better color replication. </p><p>If you are looking for something for content creation but don’t need anything as big as the Godox or the Lume Cube, consider the <a href="https://www.tomsguide.com/cameras-photography/neewer-basics-r06-7-inch-round-light-review">Neewer Basics R06 7-inch Round Light</a> for $35. The light is evenly diffused and has excellent color replication.</p><h2 class="article-body__section" id="section-lume-cube-cordless-ring-light-pro-design"><span>Lume Cube Cordless Ring Light Pro: Design </span></h2><p>The Lume Cube Cordless Ring Light Pro has a convenient design. The light looks very professional when set up, and doesn’t take much time to assemble. You simply have to set up the tripod, which extends from 18 to 70 inches, and connect the ring light to the top. With the light attached, the tripod feels well-balanced and sturdy once it has been sufficiently tightened. The ring light head alone weighs 2.4 pounds, so it can flop around if it’s too loose. </p><p>The ring light houses 256 LEDs covered with a diffuser for soft lighting with even coverage. Having a cover on the light makes it look a lot sleeker than the Godox LR120. The head of the light can tilt vertically 180º, which is useful depending on what you’re using it for. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="LtEN3NcJcpniEJjhwhPVcU" name="Lume Cube Cordless Ring Light Pro" alt="Lume Cube Cordless Ring Light Pro photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/LtEN3NcJcpniEJjhwhPVcU.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 center of the light, there’s a thread to attach the included phone holder. It could hold my <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-17-review">iPhone 17</a>, but it was a little tight. Luckily, you can attach any phone holder with a thread attachment. The holder rotates to switch easily between portrait and landscape shooting without any fuss. The thread mount is also compatible with most <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-17-review">mirrorless cameras</a>. </p><p>The light comes with a power cord and remote control. The 18-inch light comes with a travel case to store all of the components. This is padded and feels very protective. A handle also makes it easy to transport between locations if you’re on the move a lot.</p><h2 class="article-body__section" id="section-lume-cube-cordless-ring-light-pro-controls"><span>Lume Cube Cordless Ring Light Pro: Controls</span></h2><p>You can control the Lume Cube Cordless Ring Light Pro with either the onboard switches and dials or the included remote control. </p><p>There are two dials on the back of the light for brightness and color temperature, and the changes are displayed on the digital screen. However, I found it easier to make changes using the remote control, so I didn’t have to go back and forth to the light. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Hes6KTxQn7qMw6raMbc46V" name="Lume Cube Cordless Ring Light Pro" alt="Lume Cube Cordless Ring Light Pro photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/Hes6KTxQn7qMw6raMbc46V.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 remote can connect to multiple ring lights using different channel frequencies to control a studio setup if needed. This is useful if you’re shooting complex portraiture and you want to pair with some of the Lume Cube panel lights. While Lume Cube has an app for controlling some of its ring lights, it’s not compatible with the Lume Cube Cordless Ring Light Pro.</p><p>You can adjust the color temperature of the ring light between 2700K and 7500K, which is a very wide range, giving you an option for most situations. It’s a wider range than the <a href="https://www.tomsguide.com/cameras-photography/lume-cube-edge-light-2-0-review">Lume Cube Edge Light 2.0</a>, which has a range of 2700K to 7200K. The adjustment on the Cordless Ring Light Pro is stepless, so I feel like I had more control when compared to the Edge Light 2.0.</p><h2 class="article-body__section" id="section-lume-cube-cordless-ring-light-pro-performance"><span>Lume Cube Cordless Ring Light Pro: Performance</span></h2><p>The Lume Cube Cordless Ring Light Pro provided me with consistent lighting for filming video content and taking pictures. However, it’s not the best light I’ve used, and for the price, I want something that I can rely on to make me shine. </p><p>The 256 LEDs provide a maximum illumination of 1700 lux/0.5m, which is less than options by more affordable brands such as Neewer and Godox that provide illumination up to 3000Lux/0.5m. And while the Lume Cube Cordless Ring Light Pro lit me (the subject) very well, the background looked incredibly dull and lackluster.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/qvf78pnNHUTRTXvEKNhVhT.jpg" alt="Selfies taken using the Lume Cube Cordless Ring Light Pro" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/HWnAFXUAGDkgPpDePe8aNT.jpg" alt="Selfies taken using the Lume Cube Cordless Ring Light Pro" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/u7kzNRPJC2DJU5VismJqhT.jpg" alt="Selfies taken using the Lume Cube Cordless Ring Light Pro" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4en8Uoqg5q5BRmfeFyPVaT.jpg" alt="Selfies taken using the Lume Cube Cordless Ring Light Pro" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>With that said, the light has a CRI rating of 96+, which means it can excellently replicate real-world color on camera, and I would have to agree. The yellow on my t-shirt looked bright, and my complexion looked even. But the <a href="https://www.tomsguide.com/cameras-photography/neewer-basics-br60-5-ring-light-review">Neewer Basics BR60</a> does a better job of replicating color, even though it is on a much smaller scale at just 5 inches.</p><h2 class="article-body__section" id="section-lume-cube-cordless-ring-light-pro-battery-life"><span>Lume Cube Cordless Ring Light Pro: Battery life</span></h2><p>The Lume Cube Cordless Ring Light Pro can be used anywhere since it has a rechargeable lithium ion battery battery. For a more reliable wireless battery option, you can look to the <a href="https://www.tomsguide.com/cameras-photography/westcott-18-inch-bi-color-led-kit-review">Westcott 18-inch ring light</a>, which uses NP-F batteries — but this is a lot more expensive. The screen on the back of the light displays how much charge is remaining so you know when it’s time to juice up. At full brightness, you get about an hour and a half of battery life, but when using dimmer settings, you can get up to two 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="tSgHSbRsnhVmoijFeNe44V" name="Lume Cube Cordless Ring Light Pro" alt="Lume Cube Cordless Ring Light Pro photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/tSgHSbRsnhVmoijFeNe44V.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>And if you need a fixed setup for all-day use, you’re in luck, because the light also comes with an AC power adaptor. This provides more reliable lighting, and you won’t need to rush for fear of running out of battery life. </p><p>The light also has a fast-charging USB-C port at the back so you can plug your phone in and keep it charged whilst using it with the ring light. This is especially useful if you plan on using the light for live streaming.</p><h2 class="article-body__section" id="section-lume-cube-cordless-ring-light-pro-verdict"><span>Lume Cube Cordless Ring Light Pro: Verdict</span></h2><p>The Lume Cube Cordless Ring Light Pro would be a decent ring light option if it were a little cheaper. While it has some great qualities, I can’t justify the price tag when there are better options available, such as the <a href="https://www.tomsguide.com/cameras-photography/godox-lr120-review">Godox LR120</a> and the <a href="https://www.tomsguide.com/cameras-photography/neewer-rp19h-review">Neewer RP19H</a> for a fraction of the price that provide more intense illumination. </p><p>But with that said, if you’re willing to spend $300 on a middle-of-the-road ring light, there’s a lot to like. The convenient and portable build makes it easy to transport and assemble, and it’s highly adjustable to suit most eventualities. </p><p>The color accuracy with the light is great, and you can easily make adjustments with the included remote control. I also appreciate that you can use the light wirelessly or plugged into a power outlet depending on your needs. </p><p>Overall, it’s a decent light; I just can’t get on board with that price tag.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Ukraine built a military marketplace that's become 'Amazon for drones' — here's why the USA wants its own version of Brave1 ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/drones/i-spent-a-morning-on-ukraines-military-drone-marketplace-inside-the-gamified-tech-hub-powering-the-war</link>
                                                                            <description>
                            <![CDATA[ Ukraine's innovative Brave1 marketplace functions as a gamified e-commerce platform helping the country fight its ongoing war with Russia. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">evMLWGYcUaGgDXgDCiQ6uB</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/FekmQRDmJG7ENNDuvi3QQm-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 21 Jul 2026 09:00:00 +0000</pubDate>                                                                                                                                <updated>Wed, 22 Jul 2026 11:01:32 +0000</updated>
                                                                                                                                            <category><![CDATA[Drones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></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/FekmQRDmJG7ENNDuvi3QQm-1280-80.jpg">
                                                            <media:credit><![CDATA[Getty Images]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Ukranian soldiers repair a drone during the war against Russia]]></media:description>                                                            <media:text><![CDATA[Ukranian soldiers repair a drone during the war against Russia]]></media:text>
                                <media:title type="plain"><![CDATA[Ukranian soldiers repair a drone during the war against Russia]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/FekmQRDmJG7ENNDuvi3QQm-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Ukraine has been fighting its war with Russia for over a decade and the ongoing conflict has spurred innovation through brutal necessity. Nowhere is this more apparent than with the country's adoption of <a href="https://www.tomsguide.com/uk/cameras-photography/drones">drone</a> warfare.</p><p>In 2023, Ukraine launched a state-backed defense cluster designed to sync up the needs of frontline soldiers with the country's tech industry. Part of that cluster involves an e-commerce platform called the <a href="https://brave1.gov.ua/en" target="_blank">Brave1 Market</a>. Or, more commonly, "Amazon for drones".</p><p>Ukrainian soldiers can order equipment from tech manufacturers directly to their units by logging into a dashboard and spending their allocated military funding based on what they actually need. But here's where it gets interesting: units can earn digital currency (called e-points) for completing missions — whether that's reconnaissance, evacuation or neutralizing an enemy target. Different targets have different values attached and e-points can be redeemed in the marketplace for new kit.</p><p>The Ukrainian government changes these values as its priorities in the war shift. This point-scoring in turn incentivises soldiers to buy only the right equipment they need for the mission, which, in turn, creates competition and innovation among the manufacturers. Real-time intel, like telemetry or strike verification data, is also fed back to the manufacturers by the soldiers so they can patch software or refine their products to improve them. </p><p>According to <a href="https://www.defensedaily.com/army-plans-to-roll-out-amazon-like-uas-marketplace-in-early-october/army/" target="_blank">Defense Daily</a>, the Ukrainian model has been so successful that the U.S. Army wants to roll out its own version of Brave1 later this year.</p><h2 id="browsing-the-marketplace">Browsing the marketplace</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5824px;"><p class="vanilla-image-block" style="padding-top:56.04%;"><img id="6Zxg2kmQu3gxZSi5i8qdce" name="Brave1" alt="Ukraine's Brave1 drone marketplace" src="https://cdn.mos.cms.futurecdn.net/6Zxg2kmQu3gxZSi5i8qdce.jpg" mos="" align="middle" fullscreen="" width="5824" height="3264" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide/Brave1)</span></figcaption></figure><p>Despite not being enlisted in the Ukrainian military myself, there is still a huge amount of the Brave1 marketplace open to the public. I spent a morning browsing the site and, while I can't buy myself a new reconnaissance drone, I can look at specs, features and prices. </p><p>There's a huge amount of equipment available to look at, from land-based transportation drones to tiny interceptors designed to take down much larger (and more expensive) Russian drones before they can hit their target. </p><p>You can even filter down by things like which type of camera you need (nighttime or thermal imaging), payload weight as well as range and engine type.</p><p>As well as drones, soldiers can order munitions, encrypted radios, targeting tools specialized payloads and combat robots from Brave1.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5824px;"><p class="vanilla-image-block" style="padding-top:56.04%;"><img id="YEJoumKw6oveta6YPnGvee" name="Brave1 2" alt="Ukraine's Brave1 drone marketplace" src="https://cdn.mos.cms.futurecdn.net/YEJoumKw6oveta6YPnGvee.jpg" mos="" align="middle" fullscreen="" width="5824" height="3264" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide/Brave1)</span></figcaption></figure><p>While regular folks like me can't buy from the marketplace, civilian engineers, garages, and tech startups can all register their hardware or software on Brave1. If Ukraine approves it, then they can sell directly to the military.</p><p>In a January article for the <a href="https://www.usni.org/magazines/proceedings/2026/january/warfare-startup-speed-building-combat-marketplace" target="_blank">U.S. Naval Institute</a>, Marine Corps Second Lieutenant Rob Rucki noted: "Today, innovation is not just a feature of a combat system; it is a principle of warfare to be exploited for advantage. To succeed requires developing processes that enable iteration and deployment of new technologies faster than adversaries. Nowhere has this been more apparent than on the battlefields of eastern Ukraine, where the pace of innovation is driving the outcome of the fight.</p><p>"A similar innovation combat marketplace for the joint force would close the gap between frontline, company-level warfighters and private-sector producers."</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OdRkVe"></div>                            </div>                            <script src="https://kwizly.com/embed/OdRkVe.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/us/best-drones,review-2412.html">I’ve spent over 900 hours testing all the best drones: here are my top picks</a></li><li><a href="https://www.tomsguide.com/cameras-photography/drones/the-potensic-atom-3-is-a-great-beginner-drone-but-heres-why-i-think-you-should-buy-these-dji-alternatives-instead">The Potensic Atom 3 is a great beginner drone — but here’s why I think you should buy these DJI alternatives instead</a></li><li><a href="https://www.tomsguide.com/cameras-photography/drones/dji-announces-new-lito-drone-possibly-the-last-one-coming-to-the-us">DJI announces new Lito drone, possibly the last one coming to the US</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Camp Snap 2 review: Snap harder ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/camp-snap-2-review</link>
                                                                            <description>
                            <![CDATA[ The Camp Snap 2 is the upgrade I didn’t know I needed, with an improved design and built-in filters, but some issues from the Original Camp Snap linger. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">BGo3fz9n9NdH4QsLpftFnZ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/UKJvDuEP8tgDFbn5Y9LRf7-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 20 Jul 2026 13:29:10 +0000</pubDate>                                                                                                                                <updated>Mon, 20 Jul 2026 13:29:14 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras &amp; Photography]]></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/UKJvDuEP8tgDFbn5Y9LRf7-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Camp Snap 2 in Strawberry Splash]]></media:description>                                                            <media:text><![CDATA[Camp Snap 2 in Strawberry Splash]]></media:text>
                                <media:title type="plain"><![CDATA[Camp Snap 2 in Strawberry Splash]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/UKJvDuEP8tgDFbn5Y9LRf7-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Anyone who knows me will tell you that any time I have to head out to a social event, you can rest assured I’ll have a compact camera with me. And if I’m heading to a gig or on a hike, it’s usually one of the <a href="https://www.tomsguide.com/us/best-cheap-cameras,review-6319.html">best cheap cameras</a>, like the original <a href="https://www.tomsguide.com/cameras-photography/camp-snap-screen-free-digital-camera-review">Camp Snap</a>. </p><p>Camp Snap has now released the eponymous Camp Snap 2, which aims to improve on the original. But does it manage it? Well, yes and no. The camera is a little slimmer, still totally easy to use, and has zero distractions thanks to the screen-free design. It even has built-in filter options, an upgrade from the original camera. </p><p>But the Camp Snap 2 houses the same 8MP as the original, and features the same occasionally annoying shutter lag. To find out more about the camera, you can keep reading this full Camp Snap 2 review. </p><h2 class="article-body__section" id="section-camp-snap-2-review-specs"><span>Camp Snap 2 review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.campsnapphoto.com/products/camp-snap-2" target="_blank" rel="nofollow">$69</a> / <a href="https://www.campsnapphoto.com/products/camp-snap-2" target="_blank" rel="nofollow">£54</a></p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>8MP 1/3.2-inch CMOS</p></td></tr><tr><td class="firstcol " ><p><strong>Lens</strong></p></td><td  ><p>4.2mm f/2.0</p></td></tr><tr><td class="firstcol " ><p><strong>Filters</strong></p></td><td  ><p>Standard, Vintage 1-3, Black & White, Analog</p></td></tr><tr><td class="firstcol " ><p><strong>Filter thread</strong></p></td><td  ><p>30.5mm</p></td></tr><tr><td class="firstcol " ><p><strong>Flash</strong></p></td><td  ><p>LED</p></td></tr><tr><td class="firstcol " ><p><strong>MicroSD</strong></p></td><td  ><p>4GB built-in</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>Rechargeable via USB-C</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>500 shots</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>1.3 x 2.5 x 4.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>3.8 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Sunbeam Yellow, Stealth Black, Forest Green, Arctic White, Chestnut Brown, Blue Rush, Strawberry Splash, Twisted Lime, Tangerine Drift</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-camp-snap-2-review-price-availability"><span>Camp Snap 2 review: Price & availability</span></h2><p>The Camp Snap 2 is available directly from the <a href="https://www.campsnapphoto.com/products/camp-snap-2" target="_blank" rel="nofollow">Camp Snap website for $69/£54</a>. This is listed as an introductory price for the product’s launch, so if you’re planning to purchase it, the sooner the better. The original <a href="https://www.tomsguide.com/cameras-photography/camp-snap-screen-free-digital-camera-review">Camp Snap</a> camera — which is now discontinued — sold for $65, so the 2 is already (only slightly) more expensive. </p><p>Camp Snap also offers the <a href="https://www.tomsguide.com/cameras-photography/camp-snap-cs-pro-review-the-best-screen-free-digital-camera-yet">Camp Snap CS-Pro</a>, a "premium" version of the screen-free camera. This version introduced built-in filters to the cameras but came with a higher price tag of $99. The upgraded Camp Snap 2 has built-in filters, so the only real difference between it and the CS-Pro is the sensor resolution. </p><p>The Camp Snap CS-Pro comes only in a silver-and-black design that looks more like a traditional compact camera, whereas the Camp Snap 2 is a lot more whimsical. You can choose from nine different colors, including Sunbeam Yellow, Stealth Black, Forest Green, Arctic White, Chestnut Brown, Blue Rush, Strawberry Splash, Twisted Lime and Tangerine Drift. I got my hands on both Sunbeam Yellow and Strawberry Splash, and I much prefer the transparent design of the Strawberry Splash. </p><p>The Camp Snap 2 is a fun camera, just like the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-pal-review">Instax Pal</a> ($99) or the <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a> ($29), and I think $69 is a totally reasonable ask. But watch this space for the pricing post-introductory offer.</p><h2 class="article-body__section" id="section-camp-snap-2-review-design"><span>Camp Snap 2 review: Design </span></h2><p>Upon first glance, it’s pretty hard to see any differences between the original Camp Snap and the Camp Snap 2 — they’re so darn similar. The original had such a simple design that there wasn’t much to change, but the color options I mentioned above are a big win for the Camp Snap 2. </p><p>So what are the actual differences? Well, the new camera is 15% slimmer and measures 1.3 x 2.5 x 4.5 inches. Honestly, the original camera was already compact enough to slip into your purse or tuck into your pocket, so while this is a neat upgrade, it was hardly necessary. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4VDduEZs3kW3NReqVHN3R" name="Camp Snap 2" alt="Camp Snap 2 compact camera in Sunbeam Yellow" src="https://cdn.mos.cms.futurecdn.net/4VDduEZs3kW3NReqVHN3R.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 strap lugs on the sides of the camera if you want added security, but the camera is so lightweight at 3.8 ounces that it’s easy enough to just hold in one hand with a nifty ridge for your thumb to sit in. There’s also a ¼”-20 thread to attach the camera to a tripod, but I can’t imagine an instance where you’d need to utilise this, especially since the camera doesn’t have a timer option — although it’s a nice touch. </p><p>The viewfinder window now sits more centrally on the camera, in line with the lens, making it far more reliable when framing my images, and the LED flash is to the right. On the back of the camera, there’s a small screen that displays how many pictures the camera has taken, the filter being used, and the remaining battery life. This digital screen is the same as on previous models, but it works well.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3CG3rqEQQE5hMyRFMsSyQ" name="Camp Snap 2" alt="Camp Snap 2 compact camera in Sunbeam Yellow" src="https://cdn.mos.cms.futurecdn.net/3CG3rqEQQE5hMyRFMsSyQ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 silicone cover on the USB-C port on the underside of the camera for charging and image transfer. In the hatch next to the port, there’s a pre-installed 4GB microSD card which can store up to 2000 photos. You can remove this card and replace it if needed. </p><p>Most importantly, this camera has no screen. This avoids anyone thinking they don’t look good in a picture and reshooting it a billion times, which is what I love so much about the camera. If you’re used to using disposable cameras like the Fujifilm Quicksnap or the Kodak Fun Flash, you’ll be well-versed in the screenless experience and how fun it can be. But with the Camp Snap 2, there’s no waiting around for your film to be developed, and you can transfer the images to your phone at your convenience with a USB-C wire.</p><h2 class="article-body__section" id="section-camp-snap-2-review-controls"><span>Camp Snap 2 review: Controls</span></h2><p>Since this is such a simple camera, the Camp Snap 2 controls couldn’t be easier to use. There’s a switch on the back of the camera to turn it on and activate the flash. Then, to the right of the screen, there’s a small button to change the filters. This is an upgrade from the original Camp Snap, but the Camp Snap CS-Pro has a filter dial.  </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XkpDXFiTWaDfCFTFu7NPP" name="Camp Snap 2" alt="Camp Snap 2 compact camera in Sunbeam Yellow" src="https://cdn.mos.cms.futurecdn.net/XkpDXFiTWaDfCFTFu7NPP.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 filter button on the Camp Snap 2 is locked when you get the camera to avoid unwanted filter switches. To turn this off, hold the button down for 10 seconds. After this, you can cycle through the six filters with the buttons. These filters include Standard, Vintage 1 - 3, Black & White and Analog.</p><p>To make adjustments to the metadata of the camera, you can use the buttons in the hatch that stores the SD card. It’s useful to do this right away; otherwise you’ll find your images jumbled in your camera roll according to date. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="bTWQKXgpPGiiMWESE8DSF" name="Camp Snap 2" alt="Camp Snap 2 compact camera in Sunbeam Yellow" src="https://cdn.mos.cms.futurecdn.net/bTWQKXgpPGiiMWESE8DSF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 shutter button sits at the top of the camera and is easy to push even when using the camera one-handed. An issue I faced with both the original Camp Snap and the CS-Pro is a lag between pressing the shutter button and capturing the image. This has improved for the most part, but there were instances where the shutter was taking a while to engage after I pressed the button.</p><h2 class="article-body__section" id="section-camp-snap-2-review-lens-viewfinder"><span> Camp Snap 2 review: Lens & viewfinder</span></h2><p>As I briefly touched on in the design section of this article, the viewfinder is <em>finally</em> positioned above the lens on the Camp Snap 2. I feel as though I’ve been shouting into the void about this since using the original Camp Snap and subsequently the CS-Pro (which was worse!), as it didn’t match the framing of the final image at all. It’s great to at last be able to line up my pictures without a hitch. Of course, this viewfinder is just a bit of plastic, so it’s still not a perfect representation of how the final image will look, but at least now I have an accurate idea of framing. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5RZZphrYUBVAXZNDcZdEP" name="Camp Snap 2" alt="Camp Snap 2 compact camera in Sunbeam Yellow" src="https://cdn.mos.cms.futurecdn.net/5RZZphrYUBVAXZNDcZdEP.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 Camp Snap 2 is fitted with a 4.2mm fixed-focus f/2.0 lens. It offers a decently wide range of view, and I’ve found it to be great for taking group pictures as well as landscape shots. It also has a 30.5mm filter thread to attach compatible filters. </p><p>Given how simple the camera is, you don’t get any control over shutter speed or ISO, and the aperture is fixed. This can sometimes mean some images appear washed out, as you’ll see later on.</p><h2 class="article-body__section" id="section-camp-snap-2-review-image-quality"><span>Camp Snap 2 review: Image quality </span></h2><p>The Camp Snap 2 has an 8MP 1/3.2-inch CMOS sensor, which is the same resolution as the original Camp Snap. I was disappointed not to see an upgrade here since the CS-Pro houses a 16MP sensor. If image quality is your main concern, consider picking up the CS-Pro for an extra $30 instead. </p><p>As you can see in this image of flowers, the edges of the petals and leaves aren’t very crisp, and there’s not much detail across the petals. If you want something that will accurately depict what you’re seeing, 8MP just isn’t enough. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3264px;"><p class="vanilla-image-block" style="padding-top:75.00%;"><img id="Gi9FGiprwzm9ozLZYVcMaR" name="camp snap 2 flowers" alt="Tom's Guide" src="https://cdn.mos.cms.futurecdn.net/Gi9FGiprwzm9ozLZYVcMaR.jpg" mos="" align="middle" fullscreen="" width="3264" height="2448" 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>Although the images you capture aren’t going to be winning any awards, the Camp Snap 2 has been ideal for throwing in my bag and taking fun, candid pictures with my friends. During the World Cup, I took the camera to the pub with my friends to watch the football. Sadly for me, England won, as you can see from my colleague Harry’s reaction in these pictures. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/R3n3ye6XBUJ2NjBrSsYsjN.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LY8KGrjkTf2AWMjpZZjhwN.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/b76w5EQdsciHhuAYpqmHAP.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/prxSFP9UE4T5ccuTntoaVQ.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>The Camp Snap 2, as the name suggests, is also suited to outdoor adventures. However, because of the lack of onboard manual adjustments available for aperture, shutter speed and ISO, it’s hard to get a good balance of shadow and highlight when capturing an image. You're relying on the camera's auto exposure, which usually gets it wrong (although this adds to the nostalgic look).</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/RTgJ2AhTLApAVBewwbWVWV.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nZQpamr5EKjDZgSTobnZXV.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wgT57jxjzR3KGA4ZgSGcRU.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/KnPTQHSNWLk9zwHH4Hgv4T.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>The dynamic range of the Camp Sanp 2 is poor. The highlights where the sunlight was breaking through the trees looked blown out in the images, and darker shadows don’t hold a great deal of detail either when you bump up the brightness in post. </p><p>The camera is good for selfies. But be mindful since there is no screen or mirror to see yourself, you may get the framing wrong. So take a few different snaps, and definitely give the camera to the person with the longest arms. In my case, at a Pitbull gig, that was Mr. Worldwide.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/9HabndwoMHAA4RYSgRnhbS.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jQ8D7qn6AUwu9q6unJ9FfS.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ayJoQkEVYKSNjM2xnABGyP.jpg" alt="Image taken using the Camp Snap 2" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>The filters add a layer of experimentation to the camera, which I appreciate. Although the filter settings aren’t drastically different from each other, they add more personality to the pictures and mean I don’t have to edit in post. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/KYJo4DFFZHDG8Z3fbQtSbS.jpg" alt="Image taken using the Camp Snap 2" /><figcaption>Standard<small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/WCdGqGLjSDVzZ3i5H4TfFS.jpg" alt="Image taken using the Camp Snap 2" /><figcaption>Vintage 1<small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/T9PtHnP9sA8QUVXnsA8BFS.jpg" alt="Image taken using the Camp Snap 2" /><figcaption>Vintage 2<small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ebZ3GCaarDwmPpGX96RmZS.jpg" alt="Image taken using the Camp Snap 2" /><figcaption>Vintage 3<small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/5k5VD7h3PdEgcP2Pb7MjYR.jpg" alt="Image taken using the Camp Snap 2" /><figcaption>Black & White<small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/N77C6FzaYmELAoTY5W4cKS.jpg" alt="Image taken using the Camp Snap 2" /><figcaption>Analog<small role="credit">Tom's Guide</small></figcaption></figure></figure><p>As you can see in the images above, there’s a filter for most eventualities. However, I would have liked to see more cool-toned filters, since these all pull a little warm. I like the Black & White setting, but my favorite was the Analog option, as it added a vintage look to the pictures that I didn’t need to improve with edits.</p><h2 class="article-body__section" id="section-camp-snap-2-review-battery-life"><span>Camp Snap 2 review: Battery life</span></h2><p>The Camp Snap 2 has a rated battery life of 500 shots. You can see how much charge is left in the camera when you turn it on via the small digital screen. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="DzUXpbfjnGEyGktbccmHF" name="Camp Snap 2" alt="Camp Snap 2 compact camera in Sunbeam Yellow" src="https://cdn.mos.cms.futurecdn.net/DzUXpbfjnGEyGktbccmHF.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 battery drains faster when using the flash. It’s rechargeable via USB-C and takes around an hour to charge from empty. The camera automatically falls asleep if inactive, so you don’t have to waste any juice.</p><h2 class="article-body__section" id="section-camp-snap-2-review-verdict"><span>Camp Snap 2 review: Verdict</span></h2><p>The Camp Snap 2 improves on the original Camp Snap camera, even if only modestly. The compact size means it’s ideal for throwing in my bag and heading out to social gatherings or just taking quick snaps of nature when I’m out and about. The camera is easy to use with very minimal controls, and the screenless device means there are truly no distractions. It’s great to see the integration of filters with this camera, similar to those we saw on the pricier Camp Snap CS-Pro. </p><p>But if, like me, you were hoping for an image quality upgrade akin to what we get in the CS-Pro, the Camp Snap 2 becomes a bit of a let-down. The 8MP sensor is the same as in the original Camp Snap, and the same slight shutter lag lingers. </p><p>So, do you need to upgrade if you have the original Camp Snap? Absolutely not. If changing the filter is essential for you, you can edit your pictures in post. But you’re not getting improved image quality or <em>that</em> big a difference in size.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I can’t fly drones in NYC, so I let this camera robot follow me around instead and came away impressed ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/i-cant-fly-drones-in-nyc-so-i-let-this-camera-robot-follow-me-around-instead-and-came-away-impressed</link>
                                                                            <description>
                            <![CDATA[ I'm always hunting for ways to film myself on the go and Beni is a self-balancing camera robot that's one of the most useful creator tools I've tested. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">tunfhh54ZxRwEKtfbewbZi</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/8eEsN8g57DRPu9ErbLpLhF-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 20 Jul 2026 06:30:00 +0000</pubDate>                                                                                                                                <updated>Mon, 20 Jul 2026 13:44:39 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ Paul.antill@futurenet.com (Paul Antill) ]]></author>                    <dc:creator><![CDATA[ Paul Antill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/7MyNkF6oJfDnS9kZG658oC.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Paul Antill is a Video Producer and Content Creator at Tom&#039;s Guide, specializing in video/audio recording, smart home technology, and laptops. His passion lies in making tech coverage not just informative but also fun and accessible to everyone answering the question “Why does this new product or feature matter to me?” Paul has been a tech host and video producer since 2019 where he has also covered major tech and gaming events. His love for tech and video began on his&amp;nbsp;&lt;a href=&quot;https://www.youtube.com/@paulAntill&quot;&gt;&lt;strong&gt;YouTube channel&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;where for the low price of one subscription he shares head-to-head comparisons and clever ways to make the most of your gear. You can also see the behind-the-scenes and the magic that goes into our Tom’s Guide videos over on his&amp;nbsp;&lt;a href=&quot;https://www.instagram.com/thepaulantill/&quot;&gt;&lt;strong&gt;Instagram&lt;/strong&gt;&lt;/a&gt;. Paul graduated from the Mason School of Business at William &amp;amp; Mary, Williamsburg, VA, with a degree in Marketing and Business Analytics. When he&#039;s not on camera, you’ll probably still spot him behind one doing street photography and videography. Back home he’s probably in a heated Overwatch 2 match with his friends.&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/8eEsN8g57DRPu9ErbLpLhF-1280-80.jpg">
                                                            <media:credit><![CDATA[Paul Antill / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Beni on pavement]]></media:description>                                                            <media:text><![CDATA[Beni on pavement]]></media:text>
                                <media:title type="plain"><![CDATA[Beni on pavement]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/8eEsN8g57DRPu9ErbLpLhF-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As a content creator, I've spent years testing and using the <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">best drones</a> to get unique shots of myself that a camera on a tripod can't match. But now that I live in NYC, where flying a drone is mostly illegal, I've sadly had to keep my camera escapades mostly grounded. But what if I could still capture dynamic tracking clips without ever taking off? </p><p>That's the question I had with Beni, a cute two wheeled camera robot from Mondo Robotics. I've put this little guy through hell challenging it to follow me as I offroaded my E-bike, explored abandoned structures and crashed it more times than I care to admit.  </p><p>It's two adorable eyes and little chirps made me feel bad for how bad I've banged him up, but Beni has proven to me he's not just a "toy". </p><div class="instagram-embed"><blockquote class="instagram-media"  data-instgrm-version="6" style="width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><p><a href="https://www.instagram.com/p/Da8BfSwRyX8/" target="_blank">A post shared by Tom's Guide (@tomsguide)</a></p><p>A photo posted by  on </p></blockquote></div><h2 id="who-is-beni">Who is Beni?</h2><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="ZwcZ5fxZNAWicEZHGApTS7" name="Mondo Robotics - Beni-Hero" alt="Beni jumping in air" src="https://cdn.mos.cms.futurecdn.net/ZwcZ5fxZNAWicEZHGApTS7.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>Beni looks like something out of a Pixar short more than a piece of camera gear. It's small, roughly the size of a shoebox, with two big expressive eyes on its "head" that nods and shakes, and it's hard not to project a personality onto him.  </p><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="oYEaAdbiTfYmt2atnm4cWM" name="Mondo Robotics - Beni-02" alt="Beni's head & camera" src="https://cdn.mos.cms.futurecdn.net/oYEaAdbiTfYmt2atnm4cWM.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>But under that cute exterior is a surprisingly capable machine.</p><p>Beni moves on two motorized wheeled legs that bend and extend, accelerate up to 17.9 mph, lean into turns, crouch down, and spring upward, allowing him to hop curbs, perform flips or pick itself upright after it falls (which I sadly subjected him to many, many times). </p><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="uSxGXcDpM6BgqBqKNqTtz5" name="Mondo Robotics - Beni-03" alt="Beni's wheels" src="https://cdn.mos.cms.futurecdn.net/uSxGXcDpM6BgqBqKNqTtz5.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>Combine that with its built-in 4K camera that can record up to 4k 30fps or 1080p 100fps, 32GB of onboard storage (expandable with a microSD card), as well as obstacle avoidance sensors, and Beni can automatically follow me as I move around.</p><p>Manual controls are also available through a wrist-worn motion controller or the companion app which also provides a live first-person view from Beni's camera.</p><p>Beni is being pitched broadly for families, pet owners, kids or anyone who wants a fun way to capture everyday moments without holding a camera. However, I was interested in finding out how far I could push him and whether or not it was a serious hands-free video tool that could help me capture the fast-paced shots I'd normally want to use my drone for. </p><h2 id="what-is-beni-good-for">What is Beni good 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:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ou6Lx2CMi997vduy4hv9EB" name="Mondo Robotics - Beni-14" alt="Beni tracking me as I ride my E-bike" src="https://cdn.mos.cms.futurecdn.net/ou6Lx2CMi997vduy4hv9EB.png" 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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>The biggest win, by far, is speed and stability. Beni easily kept up as I rode my E-Bike, even when I picked up the pace on a longer stretch. </p><p>In its app, I can select between Beni tracking me from behind, alongside, orbiting, or following from a fixed point. A few quick clicks, and Beni was framing the shot on his own, and truly was my second camera operator. </p><p>Even for more casual walking or establishing shots, where I'd normally be limited to a tripod if I wanted to capture myself in frame, Beni can now follow me a few feet back and get the job done.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TWXxpi7ETSYHgFcyfSjc7J" name="Mondo Robotics - Beni-10" alt="Beni riding over rough terrain" src="https://cdn.mos.cms.futurecdn.net/TWXxpi7ETSYHgFcyfSjc7J.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>I thought Beni would only be able to traverse smooth roads or low cut grass, but he effectively said "hold my beer" and handled dirt, gravel, weeds and nasty bumps surprisingly well. </p><p>Yes, there were several times where this caused Beni to "trip" and lose tracking of me, but to my shock, he picked himself back up every time and was able to ride away. It got to the point where I was intentionally trying to find mounds or patches of grass that Beni would fail to get out of, but it was I who failed. I severely scrapped Beni up in the process, but he's a trooper to say the least. </p><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="78pbnCHZXn7koGKQi65fVR" name="Mondo Robotics - Beni-06" alt="Beni jumping high in the air" src="https://cdn.mos.cms.futurecdn.net/78pbnCHZXn7koGKQi65fVR.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>And then of course, there's the fun factor. The entire time I was thinking to myself, this is like a supped-up RC car with a camera attached. </p><p>Besides his very acrobatic flipping skills, I was using Beni's agility and small size to explore some "off limit" places let's say. I sent Beni into a few abandoned structures like an obedient scout. He hopped a curb, snuck under a broken fence and revealed this massive open warehouse graffitied from floor to ceiling, and it proved to be one of the most interesting shots I captured during my test. </p><h2 id="what-isn-t-beni-for">What isn't Beni 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pxF7tXVoC8isVTSpHykSAG" name="Mondo Robotics - Beni-01" alt="Beni's battle scars" src="https://cdn.mos.cms.futurecdn.net/pxF7tXVoC8isVTSpHykSAG.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>As you can probably already tell, I've been using Beni for more than his original intended purpose, and honestly, that's half the fun. That said, if I had my way or a version 2 ever came out, this is what I'd like to see.  </p><p>At its most basic, I think tracking and obstacle avoidance has room for improvement. Even without my torturous testing, Beni lost me a handful of times mid-shot, or collided head-on with stationary stuff like pillars and fences. Since he then had no idea where I was, I would have to take over by using manual controls or pull out my phone to reengage tracking. </p><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="9LreL3qwVJJscCyVUNLBpP" name="Mondo Robotics - Beni-12" alt="Beni's remote controller" src="https://cdn.mos.cms.futurecdn.net/9LreL3qwVJJscCyVUNLBpP.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>Speaking of tracking, Beni currently can only recognize people and pets. Similar to my drone, I would like to use Beni to automatically follow or orbit objects like vehicles or bikes as those are the kind of B-Roll clips I plan on using him for in the future. </p><p>Understandably, Beni's camera angle (while adjustable) is always going to be from down low. That's fine for plenty of the shots I captured, but for when I want something more eye level — it'd be interesting to see if some kind of extendable selfie stick mechanism could unlock even more. </p><p>For bonus points, I'd also love the ability to pair Beni with a wireless mic setup, similar to how my <a href="https://www.tomsguide.com/audio/dji-mic-3-review-the-gold-standard-for-content-creation">DJI mics</a> or <a href="https://www.tomsguide.com/audio/insta360-mic-air-review">Insta360 mics</a> connect directly to their respective camera systems, for the moments I want to lean on Beni as more of a primary camera instead of just for b-roll. </p><p>Am I asking for a lot? Probably. But I see Beni as a very capable tool already and know it can be used for so much more. </p><h2 id="how-much-does-beni-cost">How much does Beni cost?</h2><p>Beni is currently <a href="https://www.kickstarter.com/projects/mondorobotics/beni-all-terrain-camera-robot/pledge/new?clicked_reward=false" target="_blank">funding on Kickstarter</a>, with Super Early Bird pricing starting around $549, a solid discount off its planned $799 retail price. Like any crowdfunded product, that comes with the usual caveats, this is Mondo Robotics' first hardware campaign, so there's inherent risk around delays or features shifting before units actually ship.</p><p>Shipping is expected to begin around October 2026, initially limited to a handful of countries at launch. If you're considering backing it, treat it like a Kickstarter, not a guaranteed shelf product, and go in with that expectation.</p><h2 id="beni-has-earned-his-place-in-my-camera-kit">Beni has earned his place in my camera kit</h2><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="QyaUY4byYjNx8YWVTBTktW" name="Mondo Robotics - Beni-04" alt="Beni looking off in the distance" src="https://cdn.mos.cms.futurecdn.net/QyaUY4byYjNx8YWVTBTktW.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>Beni isn't replacing my drone or other camera gear, but he's helping me capture exciting tracking shots and leveling up my videos all while looking like it wants to be my friend. </p><p>I've crashed him more than I'd like to admit, but every time, he got back up and kept going. That's more than I can say for most of the gear I've tested.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/dji-rs-5-gimbal-review">I tested the DJI RS 5 and it's a super charged gimbal that content creators will love</a></li><li><a href="https://www.tomsguide.com/home/home-office/i-was-skeptical-about-monitor-light-bars-until-i-tried-this-one-and-now-im-never-going-back">I was skeptical about monitor light bars until I tried this one and now I'm never going back</a></li><li><a href="https://www.tomsguide.com/computing/macbooks/i-tested-the-macbook-neo-for-4k-video-editing-and-it-surprisingly-didnt-suck-until-it-did">I tested the MacBook Neo for 4K video editing and it surprisingly didn't suck — until it did</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested the Insta360 Luna Ultra for a month and it’s become my go-to camera as a content creator ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/i-tested-the-insta360-luna-ultra-for-a-month-and-its-become-my-go-to-camera-as-a-content-creator</link>
                                                                            <description>
                            <![CDATA[ The Insta360 Luna Ultra is the the perfect tool for solo content creators like myself. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">7CUnfmkaGtCuj96u6W7V2a</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/HCshefubPVRrmsUnKDhusK-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 13 Jul 2026 08:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                                    <dc:creator><![CDATA[ Nick Harris-Fry ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/J5Jjp49GUVjLZEbjEkTex.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nick has been a journalist since 2012 and has spent most of that time writing about health and fitness for a variety of publications. Nick spent nine years working on the Coach magazine and website before moving to the fitness team at Tom’s Guide in 2024. Nick is a keen runner and also the founder of YouTube channel &lt;a href=&quot;https://www.youtube.com/channel/UCOBM9FasII4dKbyE_HKkbjw&quot;&gt;The Run Testers&lt;/a&gt;, which specialises in reviewing running shoes, watches, headphones and other gear.&lt;/p&gt;&lt;p&gt;Nick has covered all aspects of health and fitness throughout his career, interviewing experts and celebrities, trying fitness classes and running marathons, all in the name of providing readers with the information they need to get the most out of an active lifestyle.&lt;/p&gt;&lt;p&gt;Nick ran his first marathon in 2016 after six weeks of training for a magazine feature and subsequently became obsessed with the sport. He now has PBs of 2hr 25min for the marathon and 15min 30sec for 5K, and has run 16 marathons in total, as well as a 50-mile ultramarathon.&lt;/p&gt;&lt;p&gt;Nick runs 60-90 miles a week and races regularly with his club, which gives him a lot of opportunity to test out running gear: he has tested and reviewed hundreds of pairs of running shoes, as well as fitness trackers, running watches, sports headphones, treadmills, and all manner of other kit. Nick is also a qualified Run Leader in the UK.&lt;/p&gt;&lt;p&gt;Nick is an established expert in the health and fitness area and along with writing for several publications, including &lt;a href=&quot;https://www.livescience.com/author/nick-harris-fry&quot;&gt;Live Science&lt;/a&gt;, &lt;a href=&quot;https://www.expertreviews.co.uk/authors/nick-harris-fry&quot;&gt;Expert Reviews&lt;/a&gt;, &lt;a href=&quot;https://www.wareable.com/author/n.harris-fry&quot;&gt;Wareable&lt;/a&gt;, &lt;a href=&quot;https://www.coachweb.com/author/nick-harris-fry&quot;&gt;Coach&lt;/a&gt; and &lt;a href=&quot;https://www.getsweatgo.com/author/n.harrisfry&quot;&gt;Get Sweat Go&lt;/a&gt;, he has been quoted on &lt;a href=&quot;https://www.theguardian.com/thefilter/2024/oct/20/if-you-pay-more-than-4-youre-being-ripped-off-the-fair-price-for-14-everyday-items-from-cleaning-spray-to-olive-oil&quot;&gt;The Guardian&lt;/a&gt; and &lt;a href=&quot;https://www.independent.co.uk/life-style/health-and-families/london-marathon-2021-date-training-tips-summer-running-a9482486.html&quot;&gt;The Independent&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Nick graduated from the University of York in 2010 with a degree in Politics, Philosophy and Economics and worked in the NHS for three years, during which time he completed his NCTJ Diploma in Journalism at News Associates in London. Before starting on Coach and moving into health and fitness, Nick worked as a football journalist and lived in Kathmandu, Nepal for two years.&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/HCshefubPVRrmsUnKDhusK-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Insta360 Luna Ultra Comp]]></media:description>                                                            <media:text><![CDATA[Insta360 Luna Ultra Comp]]></media:text>
                                <media:title type="plain"><![CDATA[Insta360 Luna Ultra Comp]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/HCshefubPVRrmsUnKDhusK-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>When I started a YouTube channel back in 2019 it’s fair to say my audiovisual skills were sorely lacking. I could review and talk about running products all day, which is what my channel <a href="https://www.youtube.com/@TheRunTesters" target="_blank">The Run Testers</a> does, but when it came to recording sound and video, I struggled with anything beyond using my phone.</p><p>I had to improve quickly, so I did, and I now have a wide array of cameras and mics I use regularly, but until I tested the new Insta360 Luna Ultra I’d never used a pocket camera with a built-in gimbal. Now I wonder how I’d ever do without one.</p><p>The Insta360 Luna Ultra aims to beat the <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review">DJI Osmo Pocket 4</a> and<a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4p-first-impressions"> DJI Osmo Pocket 4P</a> to the title of <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best camera </a>for content creators, with a dual-lens design and the ability to record up to 8K resolution video.</p><p>Having not tested the DJI cameras myself I can’t comment on the comparison, but after using the Insta360 Luna Ultra as my main camera for a month of creating videos, I’ve picked out my main likes and dislikes below. </p><p>First, some specs.</p><h3 class="article-body__section" id="section-insta360-luna-ultra-price-and-specs"><span>Insta360 Luna Ultra: Price and specs</span></h3><div class="product"><a data-dimension112="12cfa480-7b99-11f1-a91c-cbd66aa24c22" data-action="Deal Block" data-label="The Insta360 Luna Ultra is available in black or white and comes with several key accessories including a clip-on case and a connector you can use to attach it to a tripod stand." data-dimension48="The Insta360 Luna Ultra is available in black or white and comes with several key accessories including a clip-on case and a connector you can use to attach it to a tripod stand." data-dimension25="$769" href="https://www.amazon.com/Insta360-Luna-Ultra-Touchscreen-Photography-Cosmic/dp/B0GXJRJ2MK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:716px;"><p class="vanilla-image-block" style="padding-top:134.08%;"><img id="WoHznAsQer6MkADNwYLnMQ" name="Insta360 Luna Ultra_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WoHznAsQer6MkADNwYLnMQ.png" mos="" align="middle" fullscreen="" width="716" height="960" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Insta360 Luna Ultra is available in black or white and comes with several key accessories including a clip-on case and a connector you can use to attach it to a tripod stand.<a class="view-deal button" href="https://www.amazon.com/Insta360-Luna-Ultra-Touchscreen-Photography-Cosmic/dp/B0GXJRJ2MK" target="_blank" rel="nofollow" data-dimension112="12cfa480-7b99-11f1-a91c-cbd66aa24c22" data-action="Deal Block" data-label="The Insta360 Luna Ultra is available in black or white and comes with several key accessories including a clip-on case and a connector you can use to attach it to a tripod stand." data-dimension48="The Insta360 Luna Ultra is available in black or white and comes with several key accessories including a clip-on case and a connector you can use to attach it to a tripod stand." data-dimension25="$769">View Deal</a></p></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:1948px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="ZMWxYutrwffGB4co35xpZn" name="Insta360_Luna_Ultra 5A.JPG" alt="Insta360 Luna Ultra" src="https://cdn.mos.cms.futurecdn.net/v2/t:0,l:360,cw:1948,ch:1096,q:80/ZMWxYutrwffGB4co35xpZn.jpg" mos="" align="middle" fullscreen="" width="2560" height="1096" 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 Insta360 Luna Ultra comes in black or white and costs <a href="https://www.amazon.com/Insta360-Luna-Ultra-Touchscreen-Photography-Cosmic/dp/B0GXJRJ2MK" target="_blank" rel="nofollow">$769 </a>/ <a href="https://www.amazon.co.uk/Insta360-Luna-Ultra-Touchscreen-Photography/dp/B0GXJRJ2MK" target="_blank" rel="nofollow">£649</a> for the standard bundle, which includes the camera, a windguard for the built-in mic, a wrist strap and a thread handle you can use to mount it to a tripod.</p><p>I tested the Creator Bundle which adds in a battery handle, a Insta360 Mic Pro transmitter and a wide-angle lens, and costs <a href="https://www.amazon.com/Insta360-Luna-Ultra-Creator-Bundle/dp/B0GY8BS2FS" target="_blank" rel="nofollow">$969</a> / <a href="https://www.amazon.co.uk/Insta360-Luna-Ultra-Touchscreen-Photography/dp/B0GY8BS2FS" target="_blank" rel="nofollow">£829</a>.</p><div ><table><tbody><tr><td class="firstcol " ><p><strong>Starting price</strong></p></td><td  ><p><a href="https://www.amazon.com/Insta360-Luna-Ultra-Touchscreen-Photography-Cosmic/dp/B0GXJRJ2MK" target="_blank" rel="nofollow">$769 </a>/ <a href="https://www.amazon.co.uk/Insta360-Luna-Ultra-Touchscreen-Photography/dp/B0GXJRJ2MK" target="_blank" rel="nofollow">£649</a></p></td></tr><tr><td class="firstcol " ><p><strong>Max resolution (standard)</strong></p></td><td  ><p>8K/30fps</p></td></tr><tr><td class="firstcol " ><p><strong>Max resolution (slow mo)</strong></p></td><td  ><p>4K/120fps</p></td></tr><tr><td class="firstcol " ><p><strong>Sensor size</strong></p></td><td  ><p>1 inch</p></td></tr><tr><td class="firstcol " ><p><strong>Rear screen</strong></p></td><td  ><p>2-inch detachable touchscreen</p></td></tr><tr><td class="firstcol " ><p><strong>Internal memory</strong></p></td><td  ><p>47GB</p></td></tr><tr><td class="firstcol " ><p><strong>MicroSD slot</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth, Wi-Fi, USB-C</p></td></tr><tr><td class="firstcol " ><p><strong>Water resistance</strong></p></td><td  ><p>None</p></td></tr><tr><td class="firstcol " ><p><strong>Battery</strong></p></td><td  ><p>1,550 mAh</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>6.7 x 2.1 x 1.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>8.2 ounces</p></td></tr></tbody></table></div><h3 class="article-body__section" id="section-like-1-perfect-for-a-variety-of-piece-to-camera-videos"><span>Like #1: Perfect for a variety of piece-to-camera videos</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KD7QWHmwqrZdjBcgMmzgQe" name="Insta360 Luna Ultra" alt="Insta360 Luna Ultra" src="https://cdn.mos.cms.futurecdn.net/KD7QWHmwqrZdjBcgMmzgQe.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The stabilization, image quality and subject tracking on the Luna Ultra have all been excellent for me when filming pieces to camera (PTC) for my videos, in a variety of scenarios.</p><p>I shot to camera while running, walking or when sitting at a table, and the Luna Ultra worked perfectly in all scenarios. Its auto-exposure also handled changes in light well, which is useful when recording while running in a forest, for example.</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/dRZW12-JeAk" allowfullscreen></iframe></div></div><p>The Luna Ultra stands out most when you’re on the move, but even when recording at a desk the dynamic tracking makes videos more lively and engaging than using a phone or static camera. The smart tracking is notably smoother than manually adjusting the gimbal, I’ve found.</p><p>For the most part I’ve shot in landscape at 4K/30fps but I’ve tested out the 8K recording, slow-motion, and portrait modes on the camera and they’ve all looked great, even in portrait mode where you’re limited to 3K resolution (though you can turn the camera on its side to get up to 8K portrait video manually). 8K obviously gives you more flexibility in the edit, with cropping and oversampling — the DJI Osmo Pocket 4 maxes out at 4K.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/juivTnFV3Ac" allowfullscreen></iframe></div></div><h3 class="article-body__section" id="section-like-2-the-detachable-screen-remote-is-very-useful"><span>Like #2: The detachable screen remote is very useful</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:5711px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="N7qufji4vDnXe6rc9ejqSe" name="Insta360 Luna Ultra" alt="Insta360 Luna Ultra" src="https://cdn.mos.cms.futurecdn.net/N7qufji4vDnXe6rc9ejqSe.jpg" mos="" align="middle" fullscreen="" width="5711" height="3212" 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 someone who is almost always filming by myself, the detachable remote and its touchscreen are a genuine gamechanger for making shots easier to set up and capture.</p><p>Whether it’s a simple shot of some running shoes I’m testing, or using the gimbal to track and film myself running past the camera, it’s very handy for getting b-roll footage without having any trial and error in framing shots correctly.</p><h3 class="article-body__section" id="section-like-3-it-s-easy-to-use-and-has-a-fast-user-interface"><span>Like #3: It’s easy to use and has a fast user interface</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7zi6qv8p2f6qPN7bYhi7Ge" name="Insta360 Luna Ultra" alt="Insta360 Luna Ultra" src="https://cdn.mos.cms.futurecdn.net/7zi6qv8p2f6qPN7bYhi7Ge.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>I’m no tech whizz, and in the past I’ve found many cameras frustrating to use, either because the settings menus felt unintuitive or the user interface was very slow compared to using a smartphone.</p><p>I had no such problems with the Luna Ultra, which responds snappily to touch controls and is easy to adjust and switch between camera modes.</p><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="dsNbneqzFzLohkcoSySpde" name="Insta360 Luna Ultra" alt="Insta360 Luna Ultra" src="https://cdn.mos.cms.futurecdn.net/dsNbneqzFzLohkcoSySpde.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The 47GB of internal storage, three to four hour battery life, built-in mic and detachable remote all also contribute to making it a very easy camera to use out of the box, and save you time when you are using it. The Osmo Pocket 4 features a huge 107GB internal storage, which means you can record for longer without needing a card.</p><p>You get a tripod attachment and a case in the standard bundle with the Insta360 Luna Ultra, which is also useful, and the Creator Bundle also includes a battery handle and the Mic Pro, both of which are worthy additions if you’re a solo creator.</p><h3 class="article-body__section" id="section-like-4-the-built-in-mic-is-surprisingly-good-and-the-mic-pro-is-great"><span>Like #4: The built-in mic is surprisingly good, and the Mic Pro is great</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:5711px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="TNn6CBGg5Ug7Y3ME95gZoe" name="Insta360 Luna Ultra" alt="Insta360 Luna Ultra" src="https://cdn.mos.cms.futurecdn.net/TNn6CBGg5Ug7Y3ME95gZoe.jpg" mos="" align="middle" fullscreen="" width="5711" height="3212" 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 wanted to use the Luna Ultra to film videos where I talk to the camera during runs and initially did this with the Insta360 Mic Pro attached to my shirt, but the mic is a bit heavy for this and bounced around.</p><p>This prompted me to just use the built-in mic on the Luna Ultra, and I was very impressed with the sound quality. My voice is a lot clearer than when filming the same kind of videos using my phone. This isn't a surprise, as Insta360's internal camera audio has been fantastic on other cameras, like the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-x5-review">Insta360 X5</a>.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/bQdxoEgjeKc" allowfullscreen></iframe></div></div><p>That said, when you are walking or stationary, the Mic Pro is an upgrade for sound quality, and I love the e-ink display that allows you to put any kind of logo or image on the camera. </p><p>It also links with the Luna Ultra automatically and you get a prompt saying sound is now recording through the mic, which I’m always a fan of as someone who has had to redo videos in the past because my mic wasn’t actually connected to whatever camera I was using.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/XlJSdJBUINw" allowfullscreen></iframe></div></div><h3 class="article-body__section" id="section-dislike-1-you-need-pretty-big-pockets"><span>Dislike #1: You need pretty big pockets</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="zabKuJ55oLx4bhpe97tAwd" name="Insta360 Luna Ultra" alt="Insta360 Luna Ultra" src="https://cdn.mos.cms.futurecdn.net/zabKuJ55oLx4bhpe97tAwd.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The Luna Ultra is not a tiny camera, especially when you put it into its clip-on case, which I think is wise to protect the lenses and gimbal. It’s still easily portable, but tucking it into a running belt or a backpack pocket was a bit bulkier than I expected. The Osmo Pocket 4, while not exactly tiny, is a<em> lot</em> more pocketable.</p><p>Your pockets also need to be deep, because at $769 for the standard combo or $969 for the creator bundle, it’s far from cheap. DJI’s latest Osmo Pocket 4 is not available in the U.S. at the time of writing due to tariffs and the brand's continued fued with the U.S. government, but you can still get the older (and fantastic) <a href="https://www.amazon.com/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD" target="_blank" rel="nofollow">DJI Pocket 3 for $479 on Amazon</a>.</p><h3 class="article-body__section" id="section-dislike-2-you-have-to-be-careful-with-overheating"><span>Dislike #2: You have to be careful with overheating</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XHAjbGFWTtCzJDmC8Dnfhe" name="Insta360 Luna Ultra" alt="Insta360 Luna Ultra" src="https://cdn.mos.cms.futurecdn.net/XHAjbGFWTtCzJDmC8Dnfhe.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>When shooting at higher resolutions and in warm environments, I found that the Luna Ultra heated up quickly, though I never pushed it to the point it shut down to protect itself.</p><p>If I’m filming a review it can be 15 to 20 minutes long, and that was fine with the Luna Ultra in 4K mode, but you do have to be a bit wary of pushing it too hard for fear of overheating.</p><h3 class="article-body__section" id="section-dislike-3-the-autofocus-isn-t-always-perfect"><span>Dislike #3: The autofocus isn’t always perfect</span></h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="u6goNPQHMpot8UMLpRmrie" name="Insta360 Luna Ultra" alt="Insta360 Luna Ultra" src="https://cdn.mos.cms.futurecdn.net/u6goNPQHMpot8UMLpRmrie.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>On a couple of occasions when shooting using the deep tracking to follow my head, the Luna Ultra switched its focus to something behind me if I moved around a lot. I also found it didn’t correct its focus that quickly when zooming in on something at times.</p><p>These didn’t prove to be major problems over extended testing — in particular the deep tracking has almost always been infallible — but it was something I had to monitor carefully to ensure the focus was in the right place.</p><h3 class="article-body__section" id="section-verdict-ideal-for-solo-content-creators"><span>Verdict: ideal for solo content creators</span></h3><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/CYGO6N9_yL4" allowfullscreen></iframe></div></div><p>I have some minor quibbles with the Luna Ultra, but ultimately, I love it. It expands what I’m able to do when making videos solo, but it’s also a product I’m excited to use and that I enjoy using, rather than a tool for simply getting the job done.</p><p>I’m sure I’d get similar benefits from other pocket cameras, but there are some standout features to the Luna Ultra, like the detachable remote and impressive built-in mic, that make it the perfect option for solo creators.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review">I tested DJI’s new Osmo Pocket 4 — this is the only tool I'll recommend to content creators from now on</a></li><li><a href="https://www.tomsguide.com/cameras-photography/insta360s-gimbal-camera-has-just-hit-shelves-and-theres-one-factor-that-has-me-lining-up-to-purchase-one-stat">Insta360's gimbal camera has just hit shelves, and there's one factor that has me lining up to purchase one stat</a></li><li><a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">The best cameras in 2026</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ If you’re not using the best lighting for your conference calls, you’re doing it all wrong — here’s my honest opinion on this panel light for your desk ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/lume-cube-edge-light-2-0-review</link>
                                                                            <description>
                            <![CDATA[ The Lume Cube Edge Light 2.0 has leveled up my conference calls by making me look clear and polished. I just wish it were easier to move. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">xPTqmihe7c7v74wyPpEtKc</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/5L3Dg2wpWJUfSJPGwyfWXA-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 10 Jul 2026 15:45:23 +0000</pubDate>                                                                                                                                <updated>Fri, 10 Jul 2026 15:45:49 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras &amp; Photography]]></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/5L3Dg2wpWJUfSJPGwyfWXA-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[lume cube edge light 2.0]]></media:description>                                                            <media:text><![CDATA[lume cube edge light 2.0]]></media:text>
                                <media:title type="plain"><![CDATA[lume cube edge light 2.0]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/5L3Dg2wpWJUfSJPGwyfWXA-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The Lume Cube Edge Light 2.0 is one of the <a href="https://www.tomsguide.com/best-picks/best-ring-lights">best ring lights</a> I’ve tested and the perfect choice for anyone who needs reliable lighting. The accurate color replication made me look clear on video calls, and the wide range of temperature adjustments ensured I was in control of how I looked at all times. The built-in charging ports are also extremely useful if you’re short on outlet space. </p><p>So, yes, it’s one of the best options… as long as you don’t need to move it around. That’s because of the C-clamp fixture, which essentially means that once you’ve locked it in, it’s a pain to move again. However, you can also buy a version of the light with a movable base for an extra $20.</p><p>There’s also no remote for many brightness and temperature adjustments, so you have to reach wherever the base of the light is positioned, and there are no phone holders included. Depending on your needs, these could all be deal breakers, but not for me. To find out more about the light, keep reading this full Lume Cube Edge Light 2.0 review. </p><h2 class="article-body__section" id="section-lume-cube-edge-light-2-0-review-specs"><span>Lume Cube Edge Light 2.0 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/LUME-CUBE-Adjustable-Temperature-Brightness/dp/B0CN7BK69P" target="_blank" rel="nofollow">$149</a> / <a href="https://www.amazon.co.uk/Lume-Cube-Adjustable-Temperature-Brightness/dp/B0CN7BK69P" target="_blank" rel="nofollow">£147</a></p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>31 x 6 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>6.5 pounds</p></td></tr><tr><td class="firstcol " ><p><strong>Power cord length</strong></p></td><td  ><p>73 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Number of LEDs</strong></p></td><td  ><p>112</p></td></tr><tr><td class="firstcol " ><p><strong>Maximum illumination</strong></p></td><td  ><p>170 Lux/0.5m</p></td></tr><tr><td class="firstcol " ><p><strong>CRI (color rendering index)</strong></p></td><td  ><p>95+</p></td></tr><tr><td class="firstcol " ><p><strong>Color temperature</strong></p></td><td  ><p>2700K - 7500K</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-lume-cube-edge-light-2-0-review-price-availability"><span>Lume Cube Edge Light 2.0 review: Price & availability </span></h2><p>The Lume Cube Edge Light 2.0 is available at <a href="https://www.amazon.com/LUME-CUBE-Adjustable-Temperature-Brightness/dp/B0CN7BK69P" target="_blank" rel="nofollow">Amazon U.S. for $149</a> and at <a href="https://www.amazon.co.uk/Lume-Cube-Adjustable-Temperature-Brightness/dp/B0CN7BK69P" target="_blank" rel="nofollow">Amazon U.K. for £147</a>. The light comes in black or white, and while both colors look sleek, I think the black works best with my desk setup. </p><p>You can also purchase the light as a set of two at a discounted rate for <a href="https://www.amazon.com/LUME-CUBE-Adjustable-Temperature-Brightness/dp/B0CN7BK69P" target="_blank" rel="nofollow">$269 at Amazon U.S.</a> if you’re looking for a two-point lighting solution. The version of the light I tested has a clamp that secures to the edge of your desk; however, you can also purchase the light with a base for <a href="https://lumecube.com/products/lume-cube-edge-light-2-0-with-base" target="_blank" rel="nofollow">$169 from the Lume Cube online store</a>. If you have desk space available, I’d recommend opting for this option for added versatility (I'll cover this more below). </p><p>For around the same price, you can pick up the <a href="https://www.tomsguide.com/cameras-photography/neewer-rp19h-review">Neewer RP19H</a> ($149), a 19-inch ring light, but this is better for content creation and obviously takes up a lot more space. The <a href="https://www.tomsguide.com/cameras-photography/neewer-basics-r06-7-inch-round-light-review">Neewer Basics R06 7-inch Round Light</a>, on the other hand, is about the same size as the Lume Cube and a <em>lot</em> more affordable at just $35. However, the R06 doesn’t offer the same level of color temperature adjustments and has a lower maximum illumination. </p><p>The <a href="https://www.tomsguide.com/cameras-photography/godox-lr120-review">Godox LR120</a> ($59) is another worthy desk lighting option. Again, it doesn’t have the same level of adjustment as the Lume Cube, but it’s lighter and comes with built-in phone holders. </p><p>Though the Lume Cube Edge Light 2.0 is more expensive than similar-sized options from the budget brand Neewer, I think it’s worth the extra spend. The reliability of the color replication and responsive controls makes using the Lume Cube a breeze.</p><h2 class="article-body__section" id="section-lume-cube-edge-light-2-0-review-design"><span>Lume Cube Edge Light 2.0 review: Design </span></h2><p>The Lume Cube Edge Light 2.0 has given me a convenient fixed lighting solution at my desk. Although it <em>can</em> be moved, I’d call it a fixed lighting solution as it’s inconvenient to do so. The light attaches to the edge of the desk with a C-clamp, so to move it around, you need to get under the desk and unscrew it. I found it best to take some time to find the best position and just stick with it. Thankfully, you can purchase the same light with a base from the Lume Cube online store so it can be moved around more easily. I would definitely recommend that instead, as it'll perform the same, with added convenience and easier 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3EKeetmicoboi4A6hG7aXA" name="lume cube edge light 2.0" alt="lume cube edge light 2.0" src="https://cdn.mos.cms.futurecdn.net/3EKeetmicoboi4A6hG7aXA.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Since the arm can be moved around at the five pivot points, you still have some wiggle room for adjustment. The 6-inch panel light at the top rotates 360º with an arm that bends laterally at three joints, with more rotation at the base. However, I wish there were some rotation at the bend points on the arm to make micro-adjustments easier. When fully extended, the arm reaches a height of 31 inches (too tall for a desk lamp, if you ask me). It meant that if I wanted to adjust the angle of the panel light, I had to stand and reach above me, but hey, maybe I’m just short. </p><p>There are no phone holders on this light, so it’s not the best choice for mobile content creators. For that, you should look at the Godox LR120 or the <a href="https://www.tomsguide.com/cameras-photography/rotolight-ultimate-vlogging-kit-review">Rotolight Ultimate Vlogging Kit</a> ($109), which offer much more flexibility for camera fixtures. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6av2h8w7REr2UfimEqitGA" name="lume cube edge light 2.0" alt="lume cube edge light 2.0" src="https://cdn.mos.cms.futurecdn.net/6av2h8w7REr2UfimEqitGA.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 light stand, you’ll find the DC power input for the included 73-inch power cord. There’s also a USB-A output and a USB-C output that can be used simultaneously, so you don’t need to sacrifice charging points when you plug the light into an outlet. I charged my phone using these, but they’re very low-power, so it was much slower than using a power outlet with my fast charger — something worth bearing in mind.</p><h2 class="article-body__section" id="section-lume-cube-edge-light-2-0-review-controls"><span>Lume Cube Edge Light 2.0 review: Controls</span></h2><p>There are three buttons on the arm of the Lume Cube Edge Light 2.0. Unlike the Neewer RP19H, there’s no controller included with the Edge Light, so I had to lean over my desk to make any adjustments. Although not the end of the world, it made things a little tricky. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="eWn5rUoxdVXQ6M9HR9NMd9" name="lume cube edge light 2.0" alt="lume cube edge light 2.0" src="https://cdn.mos.cms.futurecdn.net/eWn5rUoxdVXQ6M9HR9NMd9.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The power button is at the bottom, and the color temperature button is in the middle. You can adjust the color temperature to 2700K, 3900K, 5100K, 6300K and 7200K. This is a wider temperature range than other lights I’ve tested, like the Neewer RP19H or the Neewer Basics R06 7-inch ring light. For smoother transitions, you can hold the button for stepless adjustment. Brightness adjustments are at the top and can be changed in increments of 20%. Again, you can have stepless adjustment by holding down the buttons. The controls were very responsive, so any changes I made were immediately reflected.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="p4Dauw9xYoHPEcajP7kWq9" name="lume cube edge light 2.0" alt="lume cube edge light 2.0" src="https://cdn.mos.cms.futurecdn.net/p4Dauw9xYoHPEcajP7kWq9.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 purchase the version of the light with the base, you’ll find touch-sensitive controls on the bottom instead of buttons on the arm. This would be much easier, so if you frequently make lighting adjustments, I’d recommend the base model.</p><h2 class="article-body__section" id="section-lume-cube-edge-light-2-0-review-performance"><span>Lume Cube Edge Light 2.0 review: Performance</span></h2><p>The Lume Cube Edge Light 2.0 provided me with solid, consistent lighting throughout my testing. I think this light is a solid choice for anyone who wants fixed lighting at their desk for at-home conferencing or streaming. </p><p>It doesn’t suffer from any bright spots as the lighting from the 112 LEDs is evenly diffused throughout the panel. This also eliminated the harshness I’ve experienced when using cooler color temperature settings on ring lights such as the Godox LR120. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="EHrDzUrNXsSWsMaeLTMDXA" name="lume cube edge light 2.0" alt="lume cube edge light 2.0" src="https://cdn.mos.cms.futurecdn.net/EHrDzUrNXsSWsMaeLTMDXA.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 a CRI (color rendering index) of 95+, the Lume Cube Edge Light 2.0 does an excellent job of making sure the lighting and colors on screen are representative of the real world. Everything from my hair color to the wall behind me looked true to life on camera. However, if you want an even higher CRI, the Neewer Basics 5-inch Ring Light’s is 97+. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/8Az9iPrB6TvkLPh8QM5JYK.jpg" alt="Lume Cube Edge Light 2.0" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LE5c5cLPtK4ZzDebDdBHeJ.jpg" alt="Lume Cube Edge Light 2.0" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/P5TL8DKVR5qnnQWekYueXK.jpg" alt="Lume Cube Edge Light 2.0" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>When using natural lighting, there’s more grain, which in turn makes the image appear flatter and of lower quality. You get more detail in your image with the Lume Cube, which I find ideal for conference calls, as it makes you more personable and professional-looking. </p><p>It’s great to have the option of warmer lighting settings as well if, like me, you tend to get a headache when using bright white lighting for extended periods. While it doesn’t look as professional on camera, color lighting has a smoothing effect on the skin.</p><h2 class="article-body__section" id="section-lume-cube-edge-light-2-0-review-verdict"><span>Lume Cube Edge Light 2.0 review: Verdict</span></h2><p>The Lume Cube Edge Light 2.0 has provided me with even lighting while sitting at my desk, as the panel does a great job of diffusing the 112 LEDs to avoid harsh lighting. This has meant I easily achieve accurate color replication. There is a wider range of temperature adjustment than in other lights I’ve tested, which is great to see, as it gives a whole lot of flexibility. The built-in charging ports also meant I didn’t feel like I was sacrificing a power outlet when plugging in the light. </p><p>But I have a couple of minor annoyances with the light. There’s no remote to adjust the lighting settings, meaning I was constantly leaning across my desk when I needed to make changes. There are no phone holders included with the light, so it’s not the best choice for short-form mobile content creation. My biggest qualm with the light is the C-clamp fixture because the light can’t be moved around easily. But it's still a great light, and you can simply buy the model with the base (rather than clamp) for much easier control and more placement freedom. </p><p>Overall, this light is a brilliant choice if you’re looking for a desk light suited to conference calls and streaming.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve been testing the Fujifilm Instax mini 13 and it’s the put the fun back into instant photography — here’s why it’s perfect for basically everyone ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-13-review</link>
                                                                            <description>
                            <![CDATA[ The Fujifilm Instax mini 13 is pure fun. It's extremely easy to use, it takes lovely photos, and it features a Close-Up mode and an accurate selfie mirror. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">HuGyhCnyU8RRafmMW4kpaN</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/upr6t6HoYUcQEk997QHPzX-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 10 Jul 2026 11:55:02 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/upr6t6HoYUcQEk997QHPzX-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A Candy Pink Fujifilm Instax mini 13 instant camera]]></media:description>                                                            <media:text><![CDATA[A Candy Pink Fujifilm Instax mini 13 instant camera]]></media:text>
                                <media:title type="plain"><![CDATA[A Candy Pink Fujifilm Instax mini 13 instant camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/upr6t6HoYUcQEk997QHPzX-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The <a href="https://www.tomsguide.com/best-picks/best-instant-cameras">best instant cameras</a> need to do three things to be considered great: they should be easy to use, they should take great photos, and above all, they should make instant photography fun. The Fujifilm Instax mini 13 does just that — and it looks stunning while doing so. Supremely easy to use, the Instax mini 13 captures and prints lovely photos, and it features a handy selfie mirror, Close-Up mode, and a self-timer.</p><p>Image and print quality are sublime as always, as is the handling. Straightforward controls, an easy-to-use app for scanning prints, and cute styling make the Instax mini 13 a fantastic instant camera. However, it still can’t shoot double exposures, and shots can sometimes come out overexposed in bright sunlight.</p><p>For the complete breakdown, read my full Fujifilm Instax mini 13 review.</p><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-specs"><span>Fujifilm Instax mini 13 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/FUJIFILM-instax-mini-Instant-Camera/dp/B0GRH779CT/" target="_blank" rel="nofollow">$93</a> / <a href="https://www.amazon.co.uk/instax-mini-13-instant-Automatic/dp/B0H15N426K/" target="_blank" rel="nofollow">£79</a></p></td></tr><tr><td class="firstcol " ><p><strong>Lens</strong></p></td><td  ><p>60mm retractable</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Monitor</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Exposure control</strong></p></td><td  ><p>Automatic</p></td></tr><tr><td class="firstcol " ><p><strong>Shutter</strong></p></td><td  ><p>1/2-1/250s (automatic)</p></td></tr><tr><td class="firstcol " ><p><strong>Aperture</strong></p></td><td  ><p>f/1.27</p></td></tr><tr><td class="firstcol " ><p><strong>Shooting range</strong></p></td><td  ><p>0.3m - ∞ (normal) | 0.3-0.5m (Close-Up)</p></td></tr><tr><td class="firstcol " ><p><strong>Flash</strong></p></td><td  ><p>Yes, built-in</p></td></tr><tr><td class="firstcol " ><p><strong>Flash refresh time</strong></p></td><td  ><p>7 seconds or less</p></td></tr><tr><td class="firstcol " ><p><strong>Flash range</strong></p></td><td  ><p>0.3-2.2m</p></td></tr><tr><td class="firstcol " ><p><strong>Self-timer</strong></p></td><td  ><p>2s, 10s</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>2x AA batteries</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>4.91 x 4.15 x 2.66 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>11.53oz (camera only)</p></td></tr><tr><td class="firstcol " ><p><strong>Film used</strong></p></td><td  ><p>Instax mini film</p></td></tr><tr><td class="firstcol " ><p><strong>Film development time</strong></p></td><td  ><p>Up to 90 seconds</p></td></tr><tr><td class="firstcol " ><p><strong>Cost per print</strong></p></td><td  ><p>80¢</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-price-availability"><span>Fujifilm Instax mini 13 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="CdYc42rojT3U8cir2Hj8zX" name="Fujifilm_Instax_Mini_13-3" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/CdYc42rojT3U8cir2Hj8zX.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>Announced in March 2026, the Fujifilm Instax mini 13 is the latest addition to the brand’s lineup of beginner-friendly instant cameras. It succeeds the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-12-review">Fujifilm Instax mini 12</a> after a three-year wait. The Instax mini 13 costs the same as its predecessor did at launch: <a href="https://www.amazon.com/FUJIFILM-instax-mini-Instant-Camera/dp/B0GRH779CT/" target="_blank" rel="nofollow">$93</a> / <a href="https://www.amazon.co.uk/instax-mini-13-instant-Automatic/dp/B0H15N426K/" target="_blank" rel="nofollow">£79 at Amazon</a>.</p><p>The Instax mini 13 is an analog instant camera, which means that all you need to do is point and shoot. There are no fancy video recording features, like on the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-evo-cinema-review">Fujifilm Instax mini Evo Cinema</a> ($409) or audio recording ones on the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-liplay-plus-review">Fujifilm Instax mini LiPlay+</a> ($234). The Instax mini 13, then, competes with the likes of the Polaroid Go Gen 3 (<a href="https://www.amazon.com/Polaroid-Go-Generation-Instant-Compatible/dp/B0GYQ93FJR/">$79</a>) and the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-41-instant-camera-review">Fujifilm Instax mini 41</a> ($99).</p><p>The Go Gen 3 is smaller and cheaper and so are its prints. The Instax mini 41 is similar to the Instax mini 13 in terms of internal hardware, but it doesn’t come in whimsical colors, and it’s a little bulkier too. If you want a cute instant camera and credit-card sized photos, the Instax mini 13 is the way to go.</p><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-design-build"><span>Fujifilm Instax mini 13 review: Design & build</span></h2><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.20%;"><img id="cLbqn2Z9apXThCaV4dT4zX" name="Fujifilm_Instax_Mini_13" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/cLbqn2Z9apXThCaV4dT4zX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1151" 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 saw the Fujifilm Instax mini 13, I thought it was an Instax mini 12 wearing a puffer jacket. Still curvy around the edges, the Instax mini 13 sports a more rounded shape compared to its predecessor. It still looks cute, and comes in a few fun colors: Candy Pink, Dreamy Purple, Frost Blue, and Lagoon Green. I tested the Candy Pink model and it looks adorable.</p><p>The only other visual difference between the two cameras is a timer lever fitted around the power button on the Instax mini 13 — and I’ll discuss it in detail shortly. Fujifilm has also included an Instax-shaped camera angle adjustment accessory attached to the camera’s wrist strap. You can put this under the Instax mini 13 to prop it up at an angle, and it’s similar to the one that was introduced with the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-wide-400-review">Fujifilm Instax Wide 400</a> ($149) back in 2024.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/rvu6ZEkFzjqdSkYKtCpjxX.jpg" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wiEZRef7VbrqKhnm2FivuX.jpg" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Handling is sublime as always — whether you’re looking through the viewfinder and photographing your friends, or holding it up with one hand to take a selfie. The Instax mini 13 is ever-so slightly heavier and bigger than the Instax mini 12. It weighs 11.53oz (versus 10.79oz) and measures 4.91 x 4.15 x 2.66 inches (versus 4.8 x 4.09 x 2.62 inches). The difference is negligible, though, and the Instax mini 13 doesn’t take up much space in handbags and backpacks.</p><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="MYyTB5YjUwZPhoDeikyUEX" name="Fujifilm_Instax_Mini_13-19" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/MYyTB5YjUwZPhoDeikyUEX.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>For those who like snapping selfies, a small selfie mirror is located on the lens. It’s the same size as the one on the Instax mini 12, and I found it accurate for framing, regardless of whether I was taking a selfie on my own or with my partner.</p><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="upr6t6HoYUcQEk997QHPzX" name="Fujifilm_Instax_Mini_13-12" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/upr6t6HoYUcQEk997QHPzX.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>Build quality has never been an issue with Fujifilm cameras, in my experience, and the Instax mini 13 isn’t an exception. The camera is built extremely well, and you can tell the plastic used in its construction is of high quality.</p><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-lens-viewfinder"><span>Fujifilm Instax mini 13 review: Lens & viewfinder</span></h2><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="UiSiZgzrjXexex2mnL9BdX" name="Fujifilm_Instax_Mini_13-5" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/UiSiZgzrjXexex2mnL9BdX.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>Optics on both the Fujifilm Instax mini 13 and the Instax mini 12 are fairly similar, if not identical. The instant camera is fitted with a retractable lens with a focal length of 60mm and a fixed f/1.27 aperture. The 60mm lens is neither too wide nor too telephoto, and it’s suited to landscape, portraits, street shots, as well as selfies.</p><p>In Normal mode, the Instax mini 13 has a focus range of 0.3m-∞, and 0.3-0.5m in Close-Up mode. The shutter speed, between 1/2s and 1/250s, is adjusted automatically and you can’t change it manually — you’ll need to spend extra on the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-99-review">Fujifilm Instax mini 99</a> ($199) for that.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="tRok7bos2VnK6mQX6ZTExX" name="Fujifilm_Instax_Mini_13-9" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/tRok7bos2VnK6mQX6ZTExX.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>But given that you don’t need to make any adjustments to the Instax mini 13’s settings, you can simply look through the 0.37x viewfinder and shoot. The viewfinder is the same size as the one on the Instax mini 12 so it’s a little small, but I didn’t have any problem looking through it and framing my shots — even as someone who wears prescription glasses.</p><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.20%;"><img id="eRfJiZ9rGvZVrhWVzqyQmX" name="Fujifilm_Instax_Mini_13-6" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/eRfJiZ9rGvZVrhWVzqyQmX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1151" 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 Instax mini 13 also features a built-in flash, located above the lens barrel. Similar to the one on its predecessor, the flash has a range of 0.3-2.2m and it can aid in low-light environments. But like on the previous gen and the Instax mini 41, the Instax mini 13’s flash can’t be turned off, so it fires every time you press the shutter button.</p><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-controls"><span>Fujifilm Instax mini 13 review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="cMiizZNk6j6YCXdSb8fGQX" name="Fujifilm_Instax_Mini_13-7" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/cMiizZNk6j6YCXdSb8fGQX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1151" 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>Fujifilm’s lineup of Instax cameras has always felt accessible and easy to use. You don’t need much prior knowledge of photography to use Instax instant cameras — and the same applies to the Instax mini 13. Controls are extremely straightforward. To turn on the camera and switch between Normal and Close-Up mode, you need to twist the ring around the lens, and you’ll know that the camera is ready to use when the orange LED lamp lights 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XcxmnZoMhSkxf3cG4oXNZX" name="Fujifilm_Instax_Mini_13-8" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/XcxmnZoMhSkxf3cG4oXNZX.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>Located next to the lens is the round shutter button which is comfortable to reach with your index or middle finger, and it gives good tactile feedback when pressed. I found it to be highly responsive in my testing, with no noticeable shutter lag. There’s a timer lever fitted around said shutter button, and this lets you set the timer to 2s or 10s. It’s easy to use, and the LED lamp flashes to indicate the countdown.</p><p>That’s about it in terms of controls, really. I reckon even a 10-year-old could use the Instax mini 13. It’s <em>that </em>simple.</p><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-image-quality"><span>Fujifilm Instax mini 13 review: Image quality</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1510px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="rwSX7ki5kdVxHxVFTWRdsJ" name="Instax-mini-13-2" alt="A photo taken and printed on a Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/rwSX7ki5kdVxHxVFTWRdsJ.jpg" mos="" align="middle" fullscreen="1" width="1510" height="850" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/rwSX7ki5kdVxHxVFTWRdsJ.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: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>The Fujifilm Instax mini 13 is a great little camera for photographing your friends and family, travels, and everyday life. There aren’t any technical differences between the Instax mini 13 and the Instax mini 12 — so if you already own the latter, there aren’t enough compelling reasons to upgrade.</p><p>Most of the photos I captured turned out lovely, with retro aesthetics and colors. The Instax mini 13 has a tendency to overexpose shots taken in bright sunlight, though, just like the Instax mini 12 and Instax mini 41 — but for the most part, it’s just a fun camera.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/DQ6bSc75UqdJCQC2GLNFqJ.jpg" alt="A photo taken and printed on a Fujifilm Instax mini 13 instant camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fLs6rujxGY7LK397xJUrhJ.jpg" alt="A photo taken and printed on a Fujifilm Instax mini 13 instant camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/RWrziJt5xBfPMXkRcXHinJ.jpg" alt="A photo taken and printed on a Fujifilm Instax mini 13 instant camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>You can see a few sample images in the gallery above. Shades of green look vivid and lush, while blues appear rich. The shadows look inky. I like how the purple flowers pop against the green leaves too.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1510px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="2na2HZ5fNjB8NVZNk3JZuJ" name="Instax-mini-13-1" alt="A photo taken and printed on a Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/2na2HZ5fNjB8NVZNk3JZuJ.jpg" mos="" align="middle" fullscreen="1" width="1510" height="850" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/2na2HZ5fNjB8NVZNk3JZuJ.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: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>The selfie mirror works as intended, enabling you to frame yourself and another person, if you wish to capture a group photo. The photo came out exactly the same as it looked in the Instax mini 13’s selfie mirror, so both my partner and I were pleased. In both selfies, our faces are in focus and they appear detailed with well-defined edges. While the exposure is well-balanced in darker lighting, images can come out overexposed with blown highlights in brighter scenes, like in the first selfie above.</p><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="minvNWujRn9v2TwG88GvjJ" name="Instax-mini-13-6" alt="A photo taken and printed on a Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/minvNWujRn9v2TwG88GvjJ.jpg" mos="" align="middle" fullscreen="1" width="1510" height="850" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/minvNWujRn9v2TwG88GvjJ.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: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>Though the Instax mini 13 has a Close-Up mode, taking those shots involves a fair bit of trial and error, as you can see above. I struggled to get the hang of distancing, but with practice, I bet I could do it right. Overall, though, the Instax mini 13 is great, and though the shots may not look very hi-res as they’ve been scanned and blown up, the credit card-sized prints themselves look plenty detailed at typical viewing distances.</p><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-app"><span>Fujifilm Instax mini 13 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="e5jSzKrFaGKuTJNSmERAFn" name="Instax-mini-13-app" alt="Instax UP! app screenshots" src="https://cdn.mos.cms.futurecdn.net/e5jSzKrFaGKuTJNSmERAFn.jpg" mos="" align="middle" fullscreen="1" width="1510" height="850" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/e5jSzKrFaGKuTJNSmERAFn.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: Fujifilm / Tom's Guide)</span></figcaption></figure><p>The Fujifilm Instax mini 13 is accompanied by the <a href="https://instax.com/up/en/" target="_blank" rel="nofollow">Instax Up!</a> app, available for free on iOS and Android. The app sports a clean interface and is straightforward to use, and it enables you to scan all your Instax prints, regardless of the size. You can then save them to your phone or create wallpapers out of them, and you can edit the brightness, contrast, saturation, and color temperature. There’s a handy Remove Reflections feature too, which works well.</p><p>I don’t own a scanner and if you don’t either, or you don’t want to spend ages scanning your prints, Instax Up! is a fantastic solution. That way, you can quickly share the prints on your social media, too.</p><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-film-cost"><span>Fujifilm Instax mini 13 review: Film cost</span></h2><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="NZT6YJ8BiDZNaBiRkHhMvW" name="Fujifilm_Instax_Mini_13-17" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/NZT6YJ8BiDZNaBiRkHhMvW.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>Just like every other camera in Fujifilm’s Instax mini lineup, the Instax mini 13 utilizes Instax mini film which generally costs <a href="https://www.amazon.com/Fujifilm-INSTAX-Instant-Twin-Pack/dp/B00EB4ADQW/" target="_blank" rel="nofollow">$16</a> / <a href="https://www.amazon.co.uk/Fujifilm-mini-instant-suitable-printers/dp/B0000C73CQ/" target="_blank" rel="nofollow">£16</a> per double film pack (20 pieces of film). That equates to approximately 80¢ per picture. Alongside the Instax mini 13, Fujifilm also launched its new Pastel Galaxy mini film, 10 exposures of which cost <a href="https://www.amazon.com/FUJIFILM-instax-Pastel-Galaxy-Instant/dp/B0G64DF9GT/" target="_blank" rel="nofollow">$18</a> / <a href="https://www.lcegroup.co.uk/New/Fujifilm-instax--Mini-Film-Pastel-Galaxy-(10-Shots)_232770.html" target="_blank" rel="nofollow">£9</a>. It’s a little expensive but it’s ideal for those who want a colorful border.</p><p>Instax mini film is a little cheaper than the Polaroid Go Color film used by the Polaroid Go Gen 3. A double pack of 16 exposures costs <a href="https://www.amazon.com/Polaroid-Go-Color-Film-Compatible/dp/B096MJ9LYK/" target="_blank" rel="nofollow">$21</a> / <a href="https://www.amazon.co.uk/Polaroid-6017-Instant-Film-double/dp/B096MJ9LYK/" target="_blank" rel="nofollow">£18</a>, making the Instax mini 13 more cost-friendly in the long run.</p><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-battery-life"><span>Fujifilm Instax mini 13 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.20%;"><img id="xoXngdMk4YGrNAVRs2UDhX" name="Fujifilm_Instax_Mini_13-11" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/xoXngdMk4YGrNAVRs2UDhX.jpg" mos="" align="middle" fullscreen="" width="2048" height="1151" 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 Fujifilm Instax mini 13 uses two AA batteries, so not much has changed in this department. Disposable batteries aren’t the best for the environment as they can’t always be recycled efficiently, but you can use rechargeable batteries if environmental impact is a concern.</p><p>Fujifilm claims that a pair of AA batteries should be able to yield 100 shots, or 10 packs of 10 exposures each — again, similar to the Instax mini 12 and the Instax mini 41. I wasn’t able to test the battery life entirely as that would involve a lot of shooting, printing and wastage of paper, but I got through four packs of prints with ease. If batteries need to be swapped, the LED lamp on the front will blink fast — or the camera simply won’t turn on.</p><h2 class="article-body__section" id="section-fujifilm-instax-mini-13-review-verdict"><span>Fujifilm Instax mini 13 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="PmesVhwYyHfbn39d98LUVX" name="Fujifilm_Instax_Mini_13-16" alt="A Candy Pink Fujifilm Instax mini 13 instant camera" src="https://cdn.mos.cms.futurecdn.net/PmesVhwYyHfbn39d98LUVX.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>If it ain’t broke, don’t fix it, but you can certainly make improvements to it. I believe that’s the mantra Fujifilm followed when designing the Instax mini 13. Not necessarily a big upgrade over the Instax mini 12, the Instax mini 13 introduces a better self-timer mechanism and features a more rounded body for better handling. Oh, and it comes in fun, cute colors too.</p><p>Image and print quality don’t give you much to complain about. Most photos turn out great (except the ones that are a little overexposed in super bright conditions) with retro, instant camera-esque color reproduction. Handling is sublime, and thanks to automatic settings adjustments, it’s the easiest instant camera to use.</p><p>If you already own its predecessor, I wouldn’t outright recommend upgrading. But if you’re picking up an instant camera for the first time, the Instax mini 13 warrants a recommendation. It makes instant photography easy and, above all, just so fun.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Forget professional photos — as a fun photography lover, I can’t recommend these 6 Prime Day cameras deals enough for any trigger-happy enthusiasts ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/forget-professional-photos-as-a-fun-photography-lover-i-cant-recommend-these-6-prime-day-cameras-deals-enough-for-any-trigger-happy-enthusiasts</link>
                                                                            <description>
                            <![CDATA[ After a fun camera to take everywhere with you? These Prime Day camera deals may be worth your while. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">mPYuBgDrYqw6TQvErWWtGW</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/qvLfgN9N9LNzCWLDpso3jM-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 10 Jul 2026 01:52:08 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ lucy.scotting@futurenet.com (Lucy Scotting) ]]></author>                    <dc:creator><![CDATA[ Lucy Scotting ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/UC9ncAYxkmJ5ipHEyX44ri.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Lucy Scotting is a staff writer for Tom’s Guide Australia, primarily covering lifestyle, streaming and internet-related news. Lucy started her career writing for HR and staffing industry publications, with articles covering emerging tech, business and finance.&lt;/p&gt;&lt;p&gt;In her spare time, Lucy can be found watching sci-fi movies, working on her dystopian fiction novel (in progress since 2017) or hanging out with her dog, Fletcher. If she’s not found doing any of the above, she’s likely on her next adventure to a new city, country, or continent. &lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/qvLfgN9N9LNzCWLDpso3jM-1280-80.jpg">
                                                            <media:credit><![CDATA[Future / Lucy Scotting]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Hand holding kodak charmera with tom&#039;s guide deal badge]]></media:description>                                                            <media:text><![CDATA[Hand holding kodak charmera with tom&#039;s guide deal badge]]></media:text>
                                <media:title type="plain"><![CDATA[Hand holding kodak charmera with tom&#039;s guide deal badge]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/qvLfgN9N9LNzCWLDpso3jM-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I'm a photography and content creator novice, so while some may take photos much more seriously than I do, I must admit I enjoy taking a quick snap. Whether it's my surroundings, a selfie or just another pic of my dog being cute, I find joy in fun photos.</p><p>Equally so, I find joy in fun cameras, which may not be classed as traditional point-and-shoots, but still grasp the essence of any moment. And right now, during <a href="https://www.tomsguide.com/live/news/amazon-prime-day-2026-au" target="_blank">Amazon Prime Day</a>, some of my top-rated fun cameras are on sale, with up to 32% off. </p><p>Earlier this year, I nabbed the cutest cam in business – the <a href="https://www.amazon.com.au/CHARMERA-Keychain-Digital-Camera-Collectable/dp/B0FKBDT8FR" target="_blank">Kodak Charmera for AU$60, but now it's down to just AU$44</a>. I adore my <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a>. It's permanently attached to my keys (and me, by extension), and while it doesn't take the best photos or videos quality-wise, it is a fun way to capture everyday moments. </p><p>I've also just gotten my hands on the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-go-3s-review">Insta360 Go 3S</a>, which has been a nice addition to my content creator set-up. I'm still a newbie, but the Go 3S is really easy to use and helps me when I'm filming solo in public or picture a gorgeous landscape in full panoramic view. Better yet, it's also <a href="https://www.amazon.com.au/Insta360-GO-Hands-Free-Stabilization-Waterproof/dp/B0D3WLV9Q1" target="_blank">discounted for Prime Day, now just AU$524.99</a>.</p><p>In addition, I've included 4 more of the best fun camera deals worth shopping on Amazon right now below, in case you're anything like me and love a fun camera. These cameras all live in the "cute", not serious photography category, and if you're after more serious recommendations, I'd suggest checking out our <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras guide</a>. </p><p>It's also worth mentioning that these deals are exclusive to Prime members, and if you're not signed up already, you can get a <a href="https://www.amazon.com.au/tryprimefree" target="_blank">free 30-day trial here</a>. You'll need to act fast to nab any of these cute Prime Day camera bargains, though, as these deals are set to expire on <strong>Monday, July 13, at 11:59pm. </strong></p><h3 class="article-body__section" id="section-best-fun-camera-deals"><span>Best fun camera deals</span></h3><div class="product"><a data-dimension112="9ed57654-7bfc-11f1-a9fe-71756773db15" data-action="Deal Block" data-label="Love the thrill of a blindbox keyring? The Kodak Charmera is worth getting your hands on. We gave it 4 stars in our review, as it is, simply put, "cute, cheap and lo-fi". You're not going to get high-definition quality out of it, but you'll get a cute keyring camera with vintage aesthetic photos and videos. The Charmera has 11 different filters and frames to choose from, and comes with a USB-C cord for easy charging. You'll only need to add a microSD card in a GB capacity of your choice to transfer your pics. Oh, and your selected colourway? That's anyone's guess, as the Charmera comes in seven mystery colours, including a "secret", rare transparent green cover." data-dimension48="Love the thrill of a blindbox keyring? The Kodak Charmera is worth getting your hands on. We gave it 4 stars in our review, as it is, simply put, "cute, cheap and lo-fi". You're not going to get high-definition quality out of it, but you'll get a cute keyring camera with vintage aesthetic photos and videos. The Charmera has 11 different filters and frames to choose from, and comes with a USB-C cord for easy charging. You'll only need to add a microSD card in a GB capacity of your choice to transfer your pics. Oh, and your selected colourway? That's anyone's guess, as the Charmera comes in seven mystery colours, including a "secret", rare transparent green cover." data-dimension25="$44" href="https://www.amazon.com.au/CHARMERA-Keychain-Digital-Camera-Collectable/dp/B0FKBDT8FR" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:465px;"><p class="vanilla-image-block" style="padding-top:71.83%;"><img id="PNVrXXCrD7c25oW9EFB4Hm" name="Screenshot 2026-01-08 at 11.12.23" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/PNVrXXCrD7c25oW9EFB4Hm.png" mos="" align="middle" fullscreen="" width="465" height="334" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Love the thrill of a blindbox keyring? The Kodak Charmera is worth getting your hands on. We gave it 4 stars in our review, as it is, simply put, "cute, cheap and lo-fi". You're not going to get high-definition quality out of it, but you'll get a cute keyring camera with vintage aesthetic photos and videos. The Charmera has 11 different filters and frames to choose from, and comes with a USB-C cord for easy charging. You'll only need to add a microSD card in a GB capacity of your choice to transfer your pics. Oh, and your selected colourway? That's anyone's guess, as the Charmera comes in seven mystery colours, including a "secret", rare transparent green cover. <a class="view-deal button" href="https://www.amazon.com.au/CHARMERA-Keychain-Digital-Camera-Collectable/dp/B0FKBDT8FR" target="_blank" rel="nofollow" data-dimension112="9ed57654-7bfc-11f1-a9fe-71756773db15" data-action="Deal Block" data-label="Love the thrill of a blindbox keyring? The Kodak Charmera is worth getting your hands on. We gave it 4 stars in our review, as it is, simply put, "cute, cheap and lo-fi". You're not going to get high-definition quality out of it, but you'll get a cute keyring camera with vintage aesthetic photos and videos. The Charmera has 11 different filters and frames to choose from, and comes with a USB-C cord for easy charging. You'll only need to add a microSD card in a GB capacity of your choice to transfer your pics. Oh, and your selected colourway? That's anyone's guess, as the Charmera comes in seven mystery colours, including a "secret", rare transparent green cover." data-dimension48="Love the thrill of a blindbox keyring? The Kodak Charmera is worth getting your hands on. We gave it 4 stars in our review, as it is, simply put, "cute, cheap and lo-fi". You're not going to get high-definition quality out of it, but you'll get a cute keyring camera with vintage aesthetic photos and videos. The Charmera has 11 different filters and frames to choose from, and comes with a USB-C cord for easy charging. You'll only need to add a microSD card in a GB capacity of your choice to transfer your pics. Oh, and your selected colourway? That's anyone's guess, as the Charmera comes in seven mystery colours, including a "secret", rare transparent green cover." data-dimension25="$44">View Deal</a></p></div><div class="product"><a data-dimension112="9ed576c2-7bfc-11f1-a36c-2b3c85585807" data-action="Deal Block" data-label="Capturing 4K content just got cuter with this tiny Insta360 Go 3S camera. It's totally magnetic, so you can stick it to most surfaces and capture 360-degree POVs. Now discounted on Amazon, this bundle is great for budding content creators, adventurers, and risk-takers on the go." data-dimension48="Capturing 4K content just got cuter with this tiny Insta360 Go 3S camera. It's totally magnetic, so you can stick it to most surfaces and capture 360-degree POVs. Now discounted on Amazon, this bundle is great for budding content creators, adventurers, and risk-takers on the go." data-dimension25="$524.99" href="https://www.amazon.com.au/Insta360-GO-Hands-Free-Stabilization-Waterproof/dp/B0D3WLV9Q1" 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="TLsFVoVyyJ4orkwY3cYceH" name="insta360-go-3s--128-gb-black-4k-tiny-por-7b8476e7-c419-4649-9706-fd95eea98f22.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TLsFVoVyyJ4orkwY3cYceH.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Capturing 4K content just got cuter with this tiny Insta360 Go 3S camera. It's totally magnetic, so you can stick it to most surfaces and capture 360-degree POVs. Now discounted on Amazon, this bundle is great for budding content creators, adventurers, and risk-takers on the go.<a class="view-deal button" href="https://www.amazon.com.au/Insta360-GO-Hands-Free-Stabilization-Waterproof/dp/B0D3WLV9Q1" target="_blank" rel="nofollow" data-dimension112="9ed576c2-7bfc-11f1-a36c-2b3c85585807" data-action="Deal Block" data-label="Capturing 4K content just got cuter with this tiny Insta360 Go 3S camera. It's totally magnetic, so you can stick it to most surfaces and capture 360-degree POVs. Now discounted on Amazon, this bundle is great for budding content creators, adventurers, and risk-takers on the go." data-dimension48="Capturing 4K content just got cuter with this tiny Insta360 Go 3S camera. It's totally magnetic, so you can stick it to most surfaces and capture 360-degree POVs. Now discounted on Amazon, this bundle is great for budding content creators, adventurers, and risk-takers on the go." data-dimension25="$524.99">View Deal</a></p></div><div class="product"><a data-dimension112="9ed5773a-7bfc-11f1-8768-811319a3307e" data-action="Deal Block" data-label="Slashing a nice 15% off, this Instax SQ1 would be a fun gift for those snap-happy folks in your life. As part of the Square lineup of Instax cameras, it produces square prints as opposed to the credit card-sized ones of the Mini 12." data-dimension48="Slashing a nice 15% off, this Instax SQ1 would be a fun gift for those snap-happy folks in your life. As part of the Square lineup of Instax cameras, it produces square prints as opposed to the credit card-sized ones of the Mini 12." data-dimension25="$169" href="https://www.amazon.com.au/Fujifilm-Instax-SQ1-Glacier-Instant/dp/B08H89FP2P" 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:85.20%;"><img id="X2Zd6opS32PLuCAmuHeePY" name="Instax SQUARE SQ1.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/X2Zd6opS32PLuCAmuHeePY.jpg" mos="" align="middle" fullscreen="" width="1500" height="1278" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Slashing a nice 15% off, this Instax SQ1 would be a fun gift for those snap-happy folks in your life. As part of the Square lineup of Instax cameras, it produces square prints as opposed to the credit card-sized ones of the Mini 12.<a class="view-deal button" href="https://www.amazon.com.au/Fujifilm-Instax-SQ1-Glacier-Instant/dp/B08H89FP2P" target="_blank" rel="nofollow" data-dimension112="9ed5773a-7bfc-11f1-8768-811319a3307e" data-action="Deal Block" data-label="Slashing a nice 15% off, this Instax SQ1 would be a fun gift for those snap-happy folks in your life. As part of the Square lineup of Instax cameras, it produces square prints as opposed to the credit card-sized ones of the Mini 12." data-dimension48="Slashing a nice 15% off, this Instax SQ1 would be a fun gift for those snap-happy folks in your life. As part of the Square lineup of Instax cameras, it produces square prints as opposed to the credit card-sized ones of the Mini 12." data-dimension25="$169">View Deal</a></p></div><div class="product"><a data-dimension112="9ed5785c-7bfc-11f1-b4d1-11a1302beddf" data-action="Deal Block" data-label="nice 4.5 stars in our review" data-dimension48="nice 4.5 stars in our review" data-dimension25="$99" href="https://www.amazon.com.au/Polaroid-Generation-Instant-Camera-Compatible/dp/B0F5HXJGDY" 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:110.00%;"><img id="zYw4RMKimdXeWMstT2Cqe4" name="Polaroid Go Gen 2 GRID" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zYw4RMKimdXeWMstT2Cqe4.jpg" mos="" align="middle" fullscreen="" width="500" height="550" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Scoring a <a href="https://www.tomsguide.com/cameras-photography/polaroid-go-gen-2-review" data-dimension112="9ed5785c-7bfc-11f1-b4d1-11a1302beddf" data-action="Deal Block" data-label="nice 4.5 stars in our review" data-dimension48="nice 4.5 stars in our review" data-dimension25="$99">nice 4.5 stars in our review</a>, the Polaroid Go Gen 2 is a cute, easy-to-use camera. Improving on the first-generation model, the Go Gen 2 introduces a new double-exposure mode for creative shots, has a self-timer and a selfie mirror, and has a decent battery life. Plus, it's supposedly so small, it'll fit in your pocket, and you can take it everywhere. The only caveat? You'll need to purchase Polaroid's compatible Go film separately, which is <a href="https://www.amazon.com.au/Polaroid-Originals-Color-Film-Compatible/dp/B096MJ9LYK" target="_blank">AU$31.41 on Amazon for a double pack</a>. <a class="view-deal button" href="https://www.amazon.com.au/Polaroid-Generation-Instant-Camera-Compatible/dp/B0F5HXJGDY" target="_blank" rel="nofollow" data-dimension112="9ed5785c-7bfc-11f1-b4d1-11a1302beddf" data-action="Deal Block" data-label="nice 4.5 stars in our review" data-dimension48="nice 4.5 stars in our review" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="9ed57942-7bfc-11f1-bb87-13b7bd1d2c52" data-action="Deal Block" data-label="The Kodak PIXPRO C1 is a highly compact digital camera and it's portable, so you can take it anywhere. There’s a dedicated flip-up screen for taking selfies, and it's perfect for anyone who wants to take pictures without taking up their phone storage." data-dimension48="The Kodak PIXPRO C1 is a highly compact digital camera and it's portable, so you can take it anywhere. There’s a dedicated flip-up screen for taking selfies, and it's perfect for anyone who wants to take pictures without taking up their phone storage." data-dimension25="$147.36" href="https://www.amazon.com.au/KODAK-Pixpro-C1-Ultra-Compact-Digital/dp/B0DY8BRMM5" 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:86.20%;"><img id="oqywc5qJBxrLyDQK4Aq5uW" name="kodak-pixpro-c1--ultracompact-digital-ca-7d0ea747-5dcc-413d-a6bf-e0ca8ebc1dbe.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/oqywc5qJBxrLyDQK4Aq5uW.jpg" mos="" align="middle" fullscreen="" width="500" height="431" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Kodak PIXPRO C1 is a highly compact digital camera and it's portable, so you can take it anywhere. There’s a dedicated flip-up screen for taking selfies, and it's perfect for anyone who wants to take pictures without taking up their phone storage. <a class="view-deal button" href="https://www.amazon.com.au/KODAK-Pixpro-C1-Ultra-Compact-Digital/dp/B0DY8BRMM5" target="_blank" rel="nofollow" data-dimension112="9ed57942-7bfc-11f1-bb87-13b7bd1d2c52" data-action="Deal Block" data-label="The Kodak PIXPRO C1 is a highly compact digital camera and it's portable, so you can take it anywhere. There’s a dedicated flip-up screen for taking selfies, and it's perfect for anyone who wants to take pictures without taking up their phone storage." data-dimension48="The Kodak PIXPRO C1 is a highly compact digital camera and it's portable, so you can take it anywhere. There’s a dedicated flip-up screen for taking selfies, and it's perfect for anyone who wants to take pictures without taking up their phone storage." data-dimension25="$147.36">View Deal</a></p></div><div class="product"><a data-dimension112="9ed579b0-7bfc-11f1-b699-a9293ae6f487" data-action="Deal Block" data-label="Camp Snap" data-dimension48="Camp Snap" data-dimension25="$89" href="https://www.amazon.com.au/Camp-Snap-Digital-Screen-Free-Adventure/dp/B0FTF8XFM2/ref=sr_1_5" 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:100.00%;"><img id="9KYhxDw5B7di6S6Mrb8FCa" name="Camp Snap" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/9KYhxDw5B7di6S6Mrb8FCa.jpg" mos="" align="middle" fullscreen="" width="1600" height="1600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>I was introduced to this camera during a family trip, and frankly, I fell in love with it. In a design similar to disposable cameras, <a href="https://www.tomsguide.com/cameras-photography/camp-snap-screen-free-digital-camera-review" data-dimension112="9ed579b0-7bfc-11f1-b699-a9293ae6f487" data-action="Deal Block" data-label="Camp Snap" data-dimension48="Camp Snap" data-dimension25="$89">Camp Snap</a> is a digital point-and-shoot without a viewfinder. It also takes photos with a similar, disposable film feel, all without needing to send the roll away for developing. What's more, it can save up to 2,000 photos with its included SD card and comes with a USB-C charging and transfer cord. <a class="view-deal button" href="https://www.amazon.com.au/Camp-Snap-Digital-Screen-Free-Adventure/dp/B0FTF8XFM2/ref=sr_1_5" target="_blank" rel="nofollow" data-dimension112="9ed579b0-7bfc-11f1-b699-a9293ae6f487" data-action="Deal Block" data-label="Camp Snap" data-dimension48="Camp Snap" data-dimension25="$89">View Deal</a></p></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ HOVERAir AQUA review: This drone is fearless over water ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/drones/hoverair-aqua-review-this-drone-is-fearless-over-water</link>
                                                                            <description>
                            <![CDATA[ The HoverAir Aqua is a highly specialized, waterproof drone that brings peace of mind when flying over lakes or pools thanks to its buoyant foam guards and self-flipping mechanism. However these features come with a high price tag, loud flight noise, and limited obstacle avoidance. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">yEEcdcUWTaXKpSWBx3Uk7T</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/6Uzk2LVnY3YHipsrkdX2DY-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 09 Jul 2026 06:15:00 +0000</pubDate>                                                                                                                                <updated>Thu, 09 Jul 2026 17:42:55 +0000</updated>
                                                                                                                                            <category><![CDATA[Drones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ Paul.antill@futurenet.com (Paul Antill) ]]></author>                    <dc:creator><![CDATA[ Paul Antill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/7MyNkF6oJfDnS9kZG658oC.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Paul Antill is a Video Producer and Content Creator at Tom&#039;s Guide, specializing in video/audio recording, smart home technology, and laptops. His passion lies in making tech coverage not just informative but also fun and accessible to everyone answering the question “Why does this new product or feature matter to me?” Paul has been a tech host and video producer since 2019 where he has also covered major tech and gaming events. His love for tech and video began on his&amp;nbsp;&lt;a href=&quot;https://www.youtube.com/@paulAntill&quot;&gt;&lt;strong&gt;YouTube channel&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;where for the low price of one subscription he shares head-to-head comparisons and clever ways to make the most of your gear. You can also see the behind-the-scenes and the magic that goes into our Tom’s Guide videos over on his&amp;nbsp;&lt;a href=&quot;https://www.instagram.com/thepaulantill/&quot;&gt;&lt;strong&gt;Instagram&lt;/strong&gt;&lt;/a&gt;. Paul graduated from the Mason School of Business at William &amp;amp; Mary, Williamsburg, VA, with a degree in Marketing and Business Analytics. When he&#039;s not on camera, you’ll probably still spot him behind one doing street photography and videography. Back home he’s probably in a heated Overwatch 2 match with his friends.&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/6Uzk2LVnY3YHipsrkdX2DY-1280-80.jpg">
                                                            <media:credit><![CDATA[Paul Antill - Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[HoverAir Aqua Flying ]]></media:description>                                                            <media:text><![CDATA[HoverAir Aqua Flying ]]></media:text>
                                <media:title type="plain"><![CDATA[HoverAir Aqua Flying ]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/6Uzk2LVnY3YHipsrkdX2DY-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">best drones</a> are continuously improving their range or camera quality, but something that really hasn't changed is their fear of deep water. If your drone loses connection, runs out of battery, or just falls out of the air, that machine isn't flying another day. But what if a drone could plunge into the water, float, and fly away without skipping a beat?</p><p>The HoverAir Aqua might have solved that. This specialized, IP67-rated drone features built-in foam prop guards that double as its personal flotation device. It is designed to take off and land directly from the water, removing the usual terror of flying over lakes, ponds and pools. I took the drone along on a recent family beach vacation to the Outer Banks (OBX), North Carolina, to see if its wet-and-wild innovations translate into a practical everyday flyer or if it's just a gimmick.</p><div class="instagram-embed"><blockquote class="instagram-media"  data-instgrm-version="6" style="width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><p><a href="https://www.instagram.com/p/DalFM4zR718/" target="_blank">A post shared by Tom's Guide (@tomsguide)</a></p><p>A photo posted by  on </p></blockquote></div><div ><table><thead><tr><th class="firstcol " ><p><strong>Price</strong></p></th><th  ><p><strong>Starts at $1299</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>249g</p></td></tr><tr><td class="firstcol " ><p><strong>Camera Sensor</strong></p></td><td  ><p>1/1.28-inch CMOS</p></td></tr><tr><td class="firstcol " ><p><strong>Video Resolution</strong></p></td><td  ><p>Up to 4K at 100fps</p></td></tr><tr><td class="firstcol " ><p><strong>Max Flight Time</strong></p></td><td  ><p>Rated up to 23 minutes</p></td></tr><tr><td class="firstcol " ><p><strong>Storage</strong></p></td><td  ><p>128GB internal</p></td></tr><tr><td class="firstcol " ><p><strong>Water resistance</strong></p></td><td  ><p>IP67</p></td></tr><tr><td class="firstcol " ><p><strong>Obstacle Avoidance</strong></p></td><td  ><p>Downward millimeter-wave radar only</p></td></tr></tbody></table></div><h2 id="hoverair-aqua-review-price-and-availability">HoverAir Aqua review: Price and availability</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/ZCG2sWjjDQs" allowfullscreen></iframe></div></div><p>Innovation doesn’t come cheap. The HoverAir Aqua starts at a relatively steep $1,299 for the Standard Combo. For this review, I tested the Fly More Combo, which retails for $1,499. This kit steps things up by adding two extra smart batteries (three total), a multi-battery charging hub, three waterproof battery bags, and the wearable Lighthouse tracker beacon.</p><p>If you want a better manual control experience, you should to step all the way up to the $1,699 Land and Sea Combo which adds HoverAir's dedicated joystick remote controller to the mix alongside the touchscreen beacon.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/avqstnD0Pbk" allowfullscreen></iframe></div></div><p>For perspective, one of my favorite drones, the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-mini-5-pro-review"><strong>DJI</strong> <strong>Mini 5 Pro</strong></a>, starts at just $709. Even with its premium Fly More Combo featuring the integrated screen remote, it maxes that'll set $1,009. That model comes with a larger 1-inch sensor, a 3-axis gimbal, and omnidirectional  obstacle avoidance. With the Aqua, you are paying a massive premium specifically for its ruggedized, buoyant engineering and making a few trade offs in return. Here's what stood out to me.</p><h3 class="article-body__section" id="section-hoverair-aqua-review-what-i-like"><span>HoverAir Aqua review: What I like</span></h3><h2 id="safely-fly-over-water">Safely fly over water</h2><p>Hands down, the best thing about the HoverAir Aqua is its buoyancy. The drone's built-in prop guards look and act like mini pool noodles, allowing it to float effortlessly on the surface of calm water. On the beaches of OBX, this completely changed how I flew. Instead of the usual anxious thoughts in the back of my head, I knew worst case, so long as I was flying relatively close to the coast, this drone would be safe.</p><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="AmkacJLRnk8EkyNiQaWzpS" name="HoverAir Aqua Flipped Over" alt="HoverAir Aqua Flipped Over" src="https://cdn.mos.cms.futurecdn.net/AmkacJLRnk8EkyNiQaWzpS.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: Paul Antill - Tom's Guide)</span></figcaption></figure><p>Even cooler is its self-righting mechanism. If a splash or a bad wave knocks the drone upside down on the water, it can flip itself right-side up and take off again. This was huge crowd-pleasing party trick during our afternoons by the pool. My younger nieces and nephews, and even my aunts and uncles absolutely got a kick when I showed this off.  </p><p>This offers incredible peace of mind if you are regularly flying over water and makes it a fantastic tool for people filming water sports like surfing or jet-skiing. Just keep a caveat in mind: limit your water landings to no-current zones. If you try it in a heavy rip current, the drone might just wash away before you can spin the props and safely take off.</p><h2 id="easy-to-operate">Easy to operate</h2><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="PVwDEva4CuZv3LibwwZhKf" name="HoverAir Aqua On Screen Controls" alt="HoverAir Aqua On Screen Controls" src="https://cdn.mos.cms.futurecdn.net/PVwDEva4CuZv3LibwwZhKf.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: Paul Antill - Tom's Guide)</span></figcaption></figure><p>There are several ways to fly and operate the Aqua. On top of the drone sits a bright, built-in 1.6-inch AMOLED screen that handles setting changes and flight mode selections directly on the aircraft, complete with clear auditory cues. </p><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="9HFuNzTFL5NeXyC7vQFskh" name="HoverAir Aqua Tracking Beacon" alt="HoverAir Aqua Tracking Beacon" src="https://cdn.mos.cms.futurecdn.net/9HFuNzTFL5NeXyC7vQFskh.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: Paul Antill - Tom's Guide)</span></figcaption></figure><p>With the wearable Lighthouse beacon that you strap to your arm, you can perform basic manual flight movements and command a Return-to-Home. This will also provide tracking (thanks to its centimeter-level RTK tracking technology) and be able to follow you as you move around. This specialized RTK tech creates a highly precise virtual tether, ensuring the camera stays locked on your position even if water spray or waves temporarily block the camera's line of sight.</p><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="FwDAQEC8M42hAmZoubdZ2L" name="HoverAir Aqua Smartphone Controls" alt="HoverAir Aqua Smartphone Controls" src="https://cdn.mos.cms.futurecdn.net/FwDAQEC8M42hAmZoubdZ2L.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: Paul Antill - Tom's Guide)</span></figcaption></figure><p>You can also connect directly via Wi-Fi with your phone, see a live view from the camera, and fly with the touch screen controls. The range is shockingly steady and long. It's great when I'm packing light and want to use this as a more traditional drone for cinematic shots from land, but it is hard to be precise with a touch screen. For those more precise controls, there's the traditional joystick controller option included in the Land and Sea Combo. This modular system snaps on both sides of your phone and gives you physical joysticks for when you need to nail more complex shots. </p><h2 id="built-in-prop-guards">Built in prop guards</h2><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="gFsRTWUB6M4vnmLjz8T2D" name="HoverAir Aqua Prop Guards" alt="HoverAir Aqua Prop Guards" src="https://cdn.mos.cms.futurecdn.net/gFsRTWUB6M4vnmLjz8T2D.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: Paul Antill - Tom's Guide)</span></figcaption></figure><p>The foam noodles pull double duty as full-coverage prop guards. It makes the Aqua feel incredibly safe to fly close to people, objects, or tight spaces, similar to the built-in guards on the DJI Neo or DJI Flip. This bumper system gave me the confidence to pull off one of my favorite drone shots to date.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/BuZJVh7iMvQ" allowfullscreen></iframe></div></div><p> I lined up the Aqua and flew it straight through the open side-split  doors of my new-to-me parked Honda Element, revealing the beautiful Shenandoah mountainside hidden right behind it. If I tried that with a standard exposed-prop drone, a tiny drift would mean shattered plastic and a ruined car headliner. </p><h3 class="article-body__section" id="section-hoverair-aqua-review-what-i-didn-t-like"><span>HoverAir Aqua review: What I didn't like</span></h3><h2 id="it-sounds-like-a-swarm-of-angry-bees">It sounds like a swarm of angry bees</h2><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="5hL3kQXuA3dqRAcg9VDYEG" name="HoverAir Aqua underside" alt="HoverAir Aqua underside" src="https://cdn.mos.cms.futurecdn.net/5hL3kQXuA3dqRAcg9VDYEG.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: Paul Antill - Tom's Guide)</span></figcaption></figure><p>If you are looking for a stealthy drone that can quietly capture cinematic landscapes, look elsewhere. Because of its smaller props, the HoverAir Aqua is incredibly loud. Compared to the relatively quiet flight profile of the newer DJI Mavic Minis, the Aqua emits a high-pitched, buzzy whirr. Whether I was flying it by the beach, near the pool, or at a local park, it instantly drew a ton of unwanted attention because it sounds exactly like a massive swarm of angry bees at lower altitudes. </p><h2 id="limited-obstacle-avoidance">Limited obstacle avoidance</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/tqSHPDwJHk0" allowfullscreen></iframe></div></div><p>Because the Aqua is primarily designed for wide-open water flight, obstacle avoidance sensors are limited to downward only. It relies on its camera system and the tracking beacon to follow you, but is limited in what it can recognize and avoid in its path. It did a very good job at following me as I ran across the open beach.</p><p>Putting it to the test, I attempted to have the drone autonomously follow me as I walked from our beach house down to the sand along a relatively narrow walkway. The Aqua did well until I walked past the gazebo to my left where it quickly collided and fell down. Thankfully, this thing is quite rugged and foam helped absorb its fall, but it proved that this drone is best for wide-open, obstacle-free spaces.</p><h2 id="shorter-battery-life">Shorter 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:5712px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VGt53wt5RkFV4XRo6mFPfT" name="HoverAir Aqua battery charging pack" alt="HoverAir Aqua battery charging pack" src="https://cdn.mos.cms.futurecdn.net/VGt53wt5RkFV4XRo6mFPfT.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: Paul Antill - Tom's Guide)</span></figcaption></figure><p>The Aqua is rated for up to 23 minutes of flight time. Once I factored in the time it takes to launch, flew out to my desired position, framed my shot, and safely head back with a comfortable battery reserve, I was realistically averaging only 15 to 18 minutes of actual flight time per battery. <br><br>While that was plenty of juice for me to get a few solid shots, it feels short because I have been completely spoiled by DJI. If you look at other standard sub-249g drones on the market, like the DJI Mini 4 Pro, I can routinely push past 30 minutes of real-world flight time on a single charge. The Aqua’s shorter endurance is the obvious tax you pay for its  waterproofing and the extra aerodynamic drag of those foam pool noodles. The thing is, while you could increased flight time with extra batteries, you'll want to make sure you swap out after drying the drone off thoroughly so that you don't get water in the battery compartment. </p><h3 class="article-body__section" id="section-hoverair-aqua-review-the-bottom-line"><span>HoverAir Aqua review: the bottom line</span></h3><p>The HoverAir Aqua is easily one of the most unique, specialized drones on the market and one of the most exciting I've tested in a long time, solving a very specific problem: making aerial photography safe and accessible over water.</p><p>That said, if you don't actually need these waterproof capabilities, the higher price tag, the deafening flight noise, and the total lack of directional obstacle avoidance are major tradeoffs you'll need to weigh.</p><p>But if your primary goal is to find a drone that can effortlessly survive a splash, take off from a lake, or safely film water activities, there is absolutely nothing else like it. It is a rugged, buoyant, and genuinely grin-inducing piece of tech that brought a massive amount of fun to our family vacation. If you're big into water sports or spend your summers by the beach, the Aqua is worth the plunge.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Potensic Atom 3 is a great beginner drone — but here’s why I think you should buy these DJI alternatives instead ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/drones/the-potensic-atom-3-is-a-great-beginner-drone-but-heres-why-i-think-you-should-buy-these-dji-alternatives-instead</link>
                                                                            <description>
                            <![CDATA[ I've enjoyed testing the Potensic Atom 3 and though it's a capable beginner drone, it pales in comparison to these DJI drones. Here's why. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">r9f5Zfs2xqvKWzzwkQo7pD</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/2EbJoewwzUzUwwqxhEbVgL-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 04 Jul 2026 08:45:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Drones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/2EbJoewwzUzUwwqxhEbVgL-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Potensic Atom 3 in the air]]></media:description>                                                            <media:text><![CDATA[Potensic Atom 3 in the air]]></media:text>
                                <media:title type="plain"><![CDATA[Potensic Atom 3 in the air]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/2EbJoewwzUzUwwqxhEbVgL-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>My job is very cool. I get to test the <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">best drones</a> which means that not only do I get to spend time in the great outdoors, but I also get to capture some incredible shots from a bird’s-eye view. You’re spoilt for choice these days as there are some incredible quadcopters available — for every budget.</p><p>DJI is, naturally, the first name that comes to mind when you hear the word “drone.” But DJI’s drones have been facing lots of scrutiny <a href="https://www.tomsguide.com/cameras-photography/drones/us-drone-ban-update">due to the ongoing ban on its quadcopters</a>. But DJI isn’t the only drone manufacturer out there — just take a look at Potensic. I’ve recently finished testing the <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-3-review">Potensic Atom 3</a>, a drone that hopes to go toe-to-toe with the DJI Lito X1, and it's excellent for beginners.</p><div class="product"><a data-dimension112="acf98550-b36e-4d3b-abcd-3de4c9142ed8" data-action="Deal Block" data-label="The Potensic Atom 3 is lightweight yet sturdy, feels more premium, and it boasts class-leading flight time. Capable of recording stable 4K video at 60fps and capturing 50MP JPEG and RAW stills, as well as boasting great AI subject tracking, the Atom 3 is a powerful drone that doesn’t give you much to complain about." data-dimension48="The Potensic Atom 3 is lightweight yet sturdy, feels more premium, and it boasts class-leading flight time. Capable of recording stable 4K video at 60fps and capturing 50MP JPEG and RAW stills, as well as boasting great AI subject tracking, the Atom 3 is a powerful drone that doesn’t give you much to complain about." data-dimension25="$429" href="https://www.amazon.com/Potensic-Transmission-Med-Tele-QuickShots-Standard/dp/B0H14KR92T/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1450px;"><p class="vanilla-image-block" style="padding-top:97.45%;"><img id="2WeRGgA6asjvme4Z4D3J2Z" name="Potensic-Atom3-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/2WeRGgA6asjvme4Z4D3J2Z.jpg" mos="" align="middle" fullscreen="" width="1450" height="1413" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Potensic Atom 3 is lightweight yet sturdy, feels more premium, and it boasts class-leading flight time. Capable of recording stable 4K video at 60fps and capturing 50MP JPEG and RAW stills, as well as boasting great AI subject tracking, the Atom 3 is a powerful drone that doesn’t give you much to complain about.<a class="view-deal button" href="https://www.amazon.com/Potensic-Transmission-Med-Tele-QuickShots-Standard/dp/B0H14KR92T/" target="_blank" rel="nofollow" data-dimension112="acf98550-b36e-4d3b-abcd-3de4c9142ed8" data-action="Deal Block" data-label="The Potensic Atom 3 is lightweight yet sturdy, feels more premium, and it boasts class-leading flight time. Capable of recording stable 4K video at 60fps and capturing 50MP JPEG and RAW stills, as well as boasting great AI subject tracking, the Atom 3 is a powerful drone that doesn’t give you much to complain about." data-dimension48="The Potensic Atom 3 is lightweight yet sturdy, feels more premium, and it boasts class-leading flight time. Capable of recording stable 4K video at 60fps and capturing 50MP JPEG and RAW stills, as well as boasting great AI subject tracking, the Atom 3 is a powerful drone that doesn’t give you much to complain about." data-dimension25="$429">View Deal</a></p></div><p>The Atom 3 improves on its predecessor, the <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-2-review">Atom 2</a>, by introducing 4K/60fps video and 50MP RAW output, making it great for color grading and flexibility in post-production. I awarded it 4-stars in my <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-3-review">Atom 3 review</a>, and while I think it’s a capable quadcopter, I’m not convinced it’s <em>the </em>best beginner drone right now… and I’d still spend on DJI.</p><h2 id="what-s-good-about-the-potensic-atom-3">What’s good about the Potensic Atom 3?</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/TicKrUWazKg" allowfullscreen></iframe></div></div><p>Good things first. The Potensic Atom 3 features a 1/1.3-inch CMOS sensor, which is larger than the 1/2-inch sensor on the Atom 2. While the Atom 2 could shoot 4K video at 30fps, the Atom 3 can do the same at 60fps, which means that motion doesn’t look as jarring anymore. And for the first time on a Potensic drone, the Atom 3 can output 50MP RAW images.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hvdPPvJevrQBBVfr5TUVnQ" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/hvdPPvJevrQBBVfr5TUVnQ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 Atom 3 also introduces the new RC PTD2 controller, so you don’t need to rely on using your smartphone as a screen anymore. The controller’s 5.5-inch touchscreen is bright at 900 nits and is easy to view and read in even direct sunlight. Flight distance has been upped from 6.21 miles on the Atom 2 to 9.94 miles on the Atom 3, too.</p><p>Lastly, the Atom 3 boasts class-leading battery life. Using the Smart Battery, you can fly for 40 minutes, and this jumps to 50 minutes when using the Smart Battery Plus. The parallel charging hub is capable of charging all three batteries at the same time, and it now comes with a USB-C port instead of a proprietary one (like on the Atom 2).</p><h2 id="what-s-not-so-great">What’s not so great?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qtCuNnmzpWPNhB5yS9DSZS" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/qtCuNnmzpWPNhB5yS9DSZS.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>You can probably tell by now that I really like the Potensic Atom 3 — after all, I awarded it 4-stars. But the thing is, it could have very easily been a 4.5-star product… if it weren’t for its fairly significant drawbacks. <strong>My biggest gripe with the Atom 3 is its lack of obstacle avoidance</strong>. This means that the drone can’t automatically avoid obstacles in its way.</p><p>I wouldn’t have marked the Atom 3 down for its lack of collision avoidance if it wasn’t for its direct competitors featuring it. The similarly priced <a href="https://www.tomsguide.com/cameras-photography/drones/dji-flip-review">DJI Flip</a> ($439) features front-facing obstacle avoidance. Now, this isn’t as good as omnidirectional avoidance, but it is <em>something</em>, and it gives the Flip a leg-up over the Atom 3. The <a href="https://www.amazon.co.uk/DJI-Beginner-Friendly-ActiveTrack-Lightweight-Transmission/dp/B0FZ9XSKKD/" target="_blank" rel="nofollow">DJI Lito X1</a> (£369), on the other hand, <em>does </em>feature omnidirectional collision sensors — but unfortunately, it isn’t available in the U.S. at the time of writing.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/Qm6vdttfQBo" allowfullscreen></iframe></div></div><p>My other primary issue with the Atom 3 is its poor low-light performance. Even though it features a 1/1.3-inch sensor, its aperture has been narrowed to f/2.8 from f/1.8 on the Atom 2, and it struggles in dimly lit environments. You’ll find the full explanation in my <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-3-review">Atom 3 review</a> but to sum it up: <strong>the Atom 3’s AI Night Mode, which is a big part of its advertisements, is simply not good enough</strong>. It overexposes the video so the “AI processed” video ends up looking a little fake. It drops the frame rate down to 25fps too, resulting in a fair amount of motion blur.</p><p>And of course, because there’s no obstacle avoidance, I wouldn’t recommend flying in low-light environments anyway, because visibility is already extremely low, and you may not always be able to maintain visual line of sight (VLOS).</p><h2 id="what-i-d-buy-instead">What I’d buy instead</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="KhbYYckf4bDhiqo5o36meL" name="Potensic_Atom_3-2" alt="Potensic Atom 3 in the air" src="https://cdn.mos.cms.futurecdn.net/KhbYYckf4bDhiqo5o36meL.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>If you’re okay with the lack of obstacle avoidance and poor low-light performance, then by all means, get the Potensic Atom 3. Everything else it offers is excellent and the drone doesn’t give you much else to complain about.</p><p>But if those are deal-breakers for you, you’ve come to the right place. Here are the DJI drones with better features I’d buy instead — and most of them are still available in the U.S.!</p><div class="product"><a data-dimension112="3b92c794-c50e-4006-a225-462de08a8471" data-action="Deal Block" data-label="DJI Mini 5 Pro" data-dimension48="DJI Mini 5 Pro" data-dimension25="$759" href="https://www.amazon.com/DJI-Beginners-Omnidirectional-ActiveTrack-Batteries/dp/B0F6XJ7W9M/" 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.80%;"><img id="oey3DVjRPNTJAX3h9syZuD" name="DJI-Mini-5-Pro deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/oey3DVjRPNTJAX3h9syZuD.jpg" mos="" align="middle" fullscreen="" width="1500" height="1422" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/drones/dji-mini-5-pro-review" data-dimension112="3b92c794-c50e-4006-a225-462de08a8471" data-action="Deal Block" data-label="DJI Mini 5 Pro" data-dimension48="DJI Mini 5 Pro" data-dimension25="$759">DJI Mini 5 Pro</a> sets a new standard for safety and imaging quality in mini drones. With omnidirectional obstacle avoidance and forward-facing LiDAR, the drone is capable of returning home even without GPS. It introduces a few notable upgrades as compared to its predecessor: a 1-inch sensor for improved low-light performance, 225° gimbal rotation, all while retaining a compact form factor.<a class="view-deal button" href="https://www.amazon.com/DJI-Beginners-Omnidirectional-ActiveTrack-Batteries/dp/B0F6XJ7W9M/" target="_blank" rel="nofollow" data-dimension112="3b92c794-c50e-4006-a225-462de08a8471" data-action="Deal Block" data-label="DJI Mini 5 Pro" data-dimension48="DJI Mini 5 Pro" data-dimension25="$759">View Deal</a></p></div><div class="product"><a data-dimension112="00ae3de7-ee29-4f22-938c-a798f88f37e8" data-action="Deal Block" data-label="DJI Neo 2" data-dimension48="DJI Neo 2" data-dimension25="$259" href="https://www.amazon.com/DJI-ActiveTrack-Omnidirectional-Obstacle-Batteries/dp/B0FJ1S18HF/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1066px;"><p class="vanilla-image-block" style="padding-top:73.17%;"><img id="qR2QDyEkqYwvExK2rTT6Qe" name="DJI-Neo-2-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/qR2QDyEkqYwvExK2rTT6Qe.png" mos="" align="middle" fullscreen="" width="1066" height="780" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/drones/dji-neo-2-review" data-dimension112="00ae3de7-ee29-4f22-938c-a798f88f37e8" data-action="Deal Block" data-label="DJI Neo 2" data-dimension48="DJI Neo 2" data-dimension25="$259">DJI Neo 2</a> is the best budget drone on the market right now, upstaging its predecessor in every way and doing so at (still) a low price point. Featuring 4K/60fps video, omnidirectional obstacle avoidance, bigger internal storage, Gesture Control, and more, it makes an already formidable drone a must-have for all pilots.<a class="view-deal button" href="https://www.amazon.com/DJI-ActiveTrack-Omnidirectional-Obstacle-Batteries/dp/B0FJ1S18HF/" target="_blank" rel="nofollow" data-dimension112="00ae3de7-ee29-4f22-938c-a798f88f37e8" data-action="Deal Block" data-label="DJI Neo 2" data-dimension48="DJI Neo 2" data-dimension25="$259">View Deal</a></p></div><div class="product"><a data-dimension112="63ff7750-9970-49ee-aba1-36e1ebdfb923" data-action="Deal Block" data-label="DJI Flip" data-dimension48="DJI Flip" data-dimension25="$349" href="https://www.amazon.com/DJI-Stabilization-44000ft-Transmission-Intelligent/dp/B0DJ1H6KCP/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1438px;"><p class="vanilla-image-block" style="padding-top:100.14%;"><img id="ETuF4Lpck8ZdBpTP77vEiU" name="DJI Flip deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ETuF4Lpck8ZdBpTP77vEiU.png" mos="" align="middle" fullscreen="" width="1438" height="1440" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/drones/dji-flip-review" data-dimension112="63ff7750-9970-49ee-aba1-36e1ebdfb923" data-action="Deal Block" data-label="DJI Flip" data-dimension48="DJI Flip" data-dimension25="$349">DJI Flip</a> is a solid mid-range drone, perfect for those looking to upgrade from older DJI models. It captures detailed 48MP stills in all lighting conditions, and records gorgeous 4K/60fps footage. It’s great for content creators as it features many shooting modes, plus it’s built really well and sports a slick design.<a class="view-deal button" href="https://www.amazon.com/DJI-Stabilization-44000ft-Transmission-Intelligent/dp/B0DJ1H6KCP/" target="_blank" rel="nofollow" data-dimension112="63ff7750-9970-49ee-aba1-36e1ebdfb923" data-action="Deal Block" data-label="DJI Flip" data-dimension48="DJI Flip" data-dimension25="$349">View Deal</a></p></div><div class="product"><a data-dimension112="6f5b9ac3-7e53-4785-8f3e-9aa68cbca9e9" data-action="Deal Block" data-label="The DJI Lito X1 features a 1/1.3-inch CMOS sensor and can record 4K/60fps HDR video as well as capture 48MP stills. It's not available in the U.S. but if you're reading this in the U.K then for the low price of £369, you're getting omnidirectional obstacle avoidance, 52-minutes of flight time, and excellent AI subject tracking." data-dimension48="The DJI Lito X1 features a 1/1.3-inch CMOS sensor and can record 4K/60fps HDR video as well as capture 48MP stills. It's not available in the U.S. but if you're reading this in the U.K then for the low price of £369, you're getting omnidirectional obstacle avoidance, 52-minutes of flight time, and excellent AI subject tracking." data-dimension25="£369" href="https://www.amazon.co.uk/DJI-Beginner-Friendly-ActiveTrack-Lightweight-Transmission/dp/B0FZ9XSKKD/" 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:51.33%;"><img id="Pw4RYSZZauxj8oLBpZx5e9" name="DJI Lito X1 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Pw4RYSZZauxj8oLBpZx5e9.jpg" mos="" align="middle" fullscreen="" width="1500" height="770" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The DJI Lito X1 features a 1/1.3-inch CMOS sensor and can record 4K/60fps HDR video as well as capture 48MP stills. It's not available in the U.S. but if you're reading this in the U.K then for the low price of £369, you're getting omnidirectional obstacle avoidance, 52-minutes of flight time, and excellent AI subject tracking.<a class="view-deal button" href="https://www.amazon.co.uk/DJI-Beginner-Friendly-ActiveTrack-Lightweight-Transmission/dp/B0FZ9XSKKD/" target="_blank" rel="nofollow" data-dimension112="6f5b9ac3-7e53-4785-8f3e-9aa68cbca9e9" data-action="Deal Block" data-label="The DJI Lito X1 features a 1/1.3-inch CMOS sensor and can record 4K/60fps HDR video as well as capture 48MP stills. It's not available in the U.S. but if you're reading this in the U.K then for the low price of £369, you're getting omnidirectional obstacle avoidance, 52-minutes of flight time, and excellent AI subject tracking." data-dimension48="The DJI Lito X1 features a 1/1.3-inch CMOS sensor and can record 4K/60fps HDR video as well as capture 48MP stills. It's not available in the U.S. but if you're reading this in the U.K then for the low price of £369, you're getting omnidirectional obstacle avoidance, 52-minutes of flight time, and excellent AI subject tracking." data-dimension25="£369">View Deal</a></p></div><div class="product"><a data-dimension112="42a4827c-4950-4bb0-834a-29b587990b44" data-action="Deal Block" data-label="Another U.K option is the DJI Lito 1, which features omnidirectional obstacle sensing, a 1/2-inch CMOS sensor, 4K/60fps capabilities, and it can capture 12MP images. There's also an 8K image mode that effectively captures 48MP stills. You can also record 4K/100fps slow motion video on it." data-dimension48="Another U.K option is the DJI Lito 1, which features omnidirectional obstacle sensing, a 1/2-inch CMOS sensor, 4K/60fps capabilities, and it can capture 12MP images. There's also an 8K image mode that effectively captures 48MP stills. You can also record 4K/100fps slow motion video on it." data-dimension25="£299" href="https://www.amazon.co.uk/DJI-Beginner-Friendly-ActiveTrack-Lightweight-Transmission/dp/B0G6Z64P3W/" 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:51.40%;"><img id="7vyJ9nXLDgUHuAkJyqhcAQ" name="DJI Lito 1 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/7vyJ9nXLDgUHuAkJyqhcAQ.jpg" mos="" align="middle" fullscreen="" width="1500" height="771" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Another U.K option is the DJI Lito 1, which features omnidirectional obstacle sensing, a 1/2-inch CMOS sensor, 4K/60fps capabilities, and it can capture 12MP images. There's also an 8K image mode that effectively captures 48MP stills. You can also record 4K/100fps slow motion video on it.<a class="view-deal button" href="https://www.amazon.co.uk/DJI-Beginner-Friendly-ActiveTrack-Lightweight-Transmission/dp/B0G6Z64P3W/" target="_blank" rel="nofollow" data-dimension112="42a4827c-4950-4bb0-834a-29b587990b44" data-action="Deal Block" data-label="Another U.K option is the DJI Lito 1, which features omnidirectional obstacle sensing, a 1/2-inch CMOS sensor, 4K/60fps capabilities, and it can capture 12MP images. There's also an 8K image mode that effectively captures 48MP stills. You can also record 4K/100fps slow motion video on it." data-dimension48="Another U.K option is the DJI Lito 1, which features omnidirectional obstacle sensing, a 1/2-inch CMOS sensor, 4K/60fps capabilities, and it can capture 12MP images. There's also an 8K image mode that effectively captures 48MP stills. You can also record 4K/100fps slow motion video on it." data-dimension25="£299">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-exVPlO"></div>                            </div>                            <script src="https://kwizly.com/embed/exVPlO.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-3-review">I’ve spent two weeks testing the new Potensic Atom 3 — and this DJI alternative is one of the best beginner drones you can buy today</a></li><li><a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4p-first-impressions">The DJI Osmo Pocket 4 is already getting an update — I spent 3 days with the Osmo Pocket 4P in Malta and here's what I'm allowed to tell you (with sample footage!)</a></li><li><a href="https://www.tomsguide.com/us/best-drones,review-2412.html">The best drones in 2026</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Target's massive 4th of July sale cuts up to $600 off cameras — big savings on Canon, Nikon and more ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/targets-massive-4th-of-july-sale-cuts-up-to-usd600-off-cameras-big-savings-on-canon-nikon-and-more</link>
                                                                            <description>
                            <![CDATA[ Target's 4th of July sale has slashed prices on a range of cameras, from feature-packed DSLR models to cool instant snappers. Check out my 12 favorite bargains. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">3bYZwuJHUKiLheiGp8BLKd</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/gyY9fdEWMLHEigw3b4g8L7-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 03 Jul 2026 16:26:27 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></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/gyY9fdEWMLHEigw3b4g8L7-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikon, Fujifilm, Kodak, Sony, Canon / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of cameras on a cream background.]]></media:description>                                                            <media:text><![CDATA[A selection of cameras on a cream background.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of cameras on a cream background.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/gyY9fdEWMLHEigw3b4g8L7-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As we get ever closer to the 4th of July, the big sales keep on coming. Yesterday I covered the best <a href="https://www.tomsguide.com/tech/18-best-target-4th-of-july-tech-deals-save-big-on-headphones-laptops-and-wearables?hasComeFromProof=true" target="_blank">Target tech deals</a> and now I'm looking at the retailer's enviable selection of cameras.</p><p>If you're looking to step up from taking snaps on your smartphone, then you'll want to at least consider a DSLR. I've picked out some top models from Canon, Nikon and Sony here, across a range of different price points.</p><p>Alternatively, you might be after something much simpler. If so, I've also selected a few cool instant cameras that are just the thing for taking on your summer trips.  </p><p>There are lots more things to buy in the <a href="https://www.tomsguide.com/sales-events/best-4th-of-july-sales-2026-walmart-home-depot-more">4th of July sales</a>. If you're looking for more deals then check out our guides to <a href="https://www.tomsguide.com/home/home-appliances/best-buy-4th-of-july-sale-huge-appliance-discounts-from-ge-lg-and-samsung-save-up-to-usd1560" target="_blank">Best Buy's appliance sale</a>, as well as its <a href="https://www.tomsguide.com/tvs/4th-of-july-tv-deals-save-up-to-usd1-700-on-lg-samsung-and-more-at-best-buy" target="_blank">TV</a> and <a href="https://www.tomsguide.com/audio/bluetooth-speakers/best-buy-4th-of-july-sale-score-up-to-33-percent-off-these-9-bluetooth-speakers-from-sony-klipsch-jbl-and-more" target="_blank">audio sales </a>too.</p>        <div class="featured_product_block featured_block_hero" data-id="7a28519a-b1b6-4e03-bbb0-24d7e774332d">            <a href="https://www.target.com/p/kodak-smile-classic-digital-instant-camera-for-3-5-x-4-25-zink-photo-paper-bluetooth-16mp-pictures/-/A-82282261?preselect=82282260#lnk=sametab" data-model-name="Kodak Smile Classic Digital Instant Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/AC8Vs4gfNxRzcMWKee7958.webp" alt="Kodak Smile Classic Digital Instant Camera for 3.5 X 4.25 Zink Photo Paper - Bluetooth, 16mp Pictures (red)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Kodak</div>                                        <div class="featured__title">Kodak Smile Classic Digital Instant Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="57ca64da-74eb-421e-876a-72633ae005f7">            <a href="https://www.target.com/p/kodak-pixpro-fz55-digital-camera-red-32gb-memory-card-camera-case/-/A-1006353493#lnk=sametab" data-model-name="Kodak PIXPRO FZ55 Camera with 32GB Memory Card & Camera Case" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/GHmzZL8oME8Zn75RW7dEH4.webp" alt="Kodak Pixpro Fz55 Digital Camera Red With 32gb Memory Card & Camera Case"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Kodak</div>                                        <div class="featured__title">Kodak PIXPRO FZ55 Camera with 32GB Memory Card & Camera Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="f6205ebf-d2cc-4a4b-91b4-c76c63f8699c">            <a href="https://www.target.com/p/fujifilm-instax-mini-12-camera-blossom-pink-case-instax-mini-instant-film-100-sheets-mini-photo-album/-/A-1007330499#lnk=sametab" data-model-name="Fujifilm Instax Mini 12 Camera with Instant Film and Photo Album" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/umzweWZ2AXFxdD9cUVwnd7.webp" alt="Fujifilm Instax Mini 12 Camera (blossom Pink)   Case   Instax Mini Instant Film (100 Sheets)   Mini Photo Album"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Fujifilm</div>                                        <div class="featured__title">Fujifilm Instax Mini 12 Camera with Instant Film and Photo Album</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="12681ba5-42de-4eef-8233-f0a0573613d4">            <a href="https://www.target.com/p/canon-eos-r100-rf-s18-45mm-f4-5-6-3-is-stm-lens-kit/-/A-89177813#lnk=sametab" data-model-name="Canon EOS R100 Mirrorless Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kJtFV92p9aVnXbRg7gFNdR.webp" alt="Canon Eos R100 Rf-S18-45mm F4.5-6.3 Is Stm Lens Kit: 24.1mp Mirrorless, Wi-Fi"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Canon EOS R100 Mirrorless Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="d29abe2e-8f7f-4c0b-a05d-1be82ae1bdf2">            <a href="https://www.target.com/p/om-system-om-d-e-m10-mark-iv-black-mirrorless-camera-with-14-42mm-ez-lens-kit/-/A-1000985462#lnk=sametab" data-model-name="OM System OM-D E-M10 Mark IV Mirrorless Camera with 14-42mm EZ Lens Kit" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ebsNY4SqyPTBU7fJtrgQg9.webp" alt="Om System Om-D E-M10 Mark Iv Black Mirrorless Camera With 14-42mm Ez Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>OM System</div>                                        <div class="featured__title">OM System OM-D E-M10 Mark IV Mirrorless Camera with 14-42mm EZ Lens Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="0bc625ae-d059-469e-8714-95d2da4ffe7f">            <a href="https://www.target.com/p/canon-eos-r50-rf-s18-45mm-f4-5-6-3mm-is-stm-kit/-/A-88661030#lnk=sametab" data-model-name="Canon EOS R50 Mirrorless Digital Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/UFvqDWznhiVZNcWmoUNBe6.webp" alt="Canon Eos R50 Rf-S18-45mm F4.5-6.3mm Is Stm Kit: Mirrorless Wi-Fi Digital Camera"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Canon EOS R50 Mirrorless Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="3e94430b-90b1-400d-9a7b-1beb8d3a9c35">            <a href="https://www.target.com/p/sony-alpha-zv-e10-mirrorless-vlog-camera-with-16-50mm-f-3-5-5-6-ii-lens-black/-/A-1009791818#lnk=sametab" data-model-name="Sony Alpha ZV-E10 Mirrorless Camera with 16-50mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jbohhK55NQvx38erhntf9S.webp" alt="Sony Alpha Zv-E10 Mirrorless Vlog Camera With 16-50mm F/3.5-5.6 Ii Lens (black)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Sony</div>                                        <div class="featured__title">Sony Alpha ZV-E10 Mirrorless Camera with 16-50mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fb05059f-7f53-4390-9a9d-4917736042c8">            <a href="https://www.target.com/p/canon-eos-rp-mirrorless-digital-camera-with-24-105mm-f-4-7-1-stm-lens-kit/-/A-83999286#lnk=sametab" data-model-name="Canon EOS RP Mirrorless Digital Camera with 24-105mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/PRYXSrS7ZXnrSfAWLSCmxW.webp" alt="Canon Eos Rp Mirrorless Digital Camera With 24-105mm F/4-7.1 Stm Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Canon</div>                                        <div class="featured__title">Canon EOS RP Mirrorless Digital Camera with 24-105mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1c170288-f19c-4b5b-9d91-191f36bc6c0a">            <a href="https://www.target.com/p/om-system-om-5-mark-ii-black-mirrorless-camera-with-14-150mm-lens-kit/-/A-1006521726#lnk=sametab" data-model-name="Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rH274JEp4qE3KVmz4ziDx8.webp" alt="Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all OM System</div>                                        <div class="featured__title">Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9b14d8be-01ff-4767-93c7-74d9b58ce0ff">            <a href="https://www.target.com/p/sony-alpha-zve10-ii-aps-c-interchangeable-lens-mirrorless-designed-for-content-creators-body-only-black/-/A-84244533#lnk=sametab" data-model-name="Sony Alpha A7 III Mirrorless Camera with 28-70mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/tyKydjCtF6TBXUuSpLiQVS.webp" alt="Sony Alpha A7 Iii 24.2mp Full Frame Mirrorless Digital Camera With 28-70mm Lens"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony Alpha A7 III Mirrorless Camera with 28-70mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e1779bc6-9c01-4a08-bfea-d89cf9a20d98">            <a href="https://www.target.com/p/nikon-z5-ii-mirrorless-digital-camera-with-z-24-200mm-f-4-6-3-vr-lens/-/A-1003487515#lnk=sametab" data-model-name="Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Nahbx7MHmvTqeuhDcVk9s8.webp" alt="Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Nikon</div>                                        <div class="featured__title">Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="a0e6535a-2b3d-40ee-8226-29b8f030c5f1">            <a href="https://www.target.com/p/nikon-zf-mirrorless-camera-with-24-70mm-f-4-lens-24-5mp-4k-uhd-video-silver/-/A-1007130727#lnk=sametab" data-model-name="Nikon Zf Mirrorless Camera with 24-70mm F/4 Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/GLn79FhaFACLS65qjRv877.webp" alt="Nikon Zf Mirrorless Camera With 24-70mm F/4 Lens - 24.5mp, 4k Uhd Video (silver)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nikon</div>                                        <div class="featured__title">Nikon Zf Mirrorless Camera with 24-70mm F/4 Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve spent two weeks testing the new Potensic Atom 3 — and this DJI alternative is one of the best beginner drones you can buy today ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/drones/potensic-atom-3-review</link>
                                                                            <description>
                            <![CDATA[ The Potensic Atom 3 is a powerful beginner-friendly drone that shoots 4K/60fps video and 50MP stills, and it's easy to fly thanks to the new PTD2 controller. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">JbXSNYfrmtgPUiKFxfHw4L</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/qtCuNnmzpWPNhB5yS9DSZS-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 01 Jul 2026 14:24:23 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Drones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/qtCuNnmzpWPNhB5yS9DSZS-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Potensic Atom 3 4K drone]]></media:description>                                                            <media:text><![CDATA[Potensic Atom 3 4K drone]]></media:text>
                                <media:title type="plain"><![CDATA[Potensic Atom 3 4K drone]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/qtCuNnmzpWPNhB5yS9DSZS-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Good becomes great, great becomes outstanding: this seems to be the trajectory Potensic is currently on. The Potensic Atom 3 retains its predecessor’s lightweight body but is built better and feels more robust. The Atom 2 is now capable of recording 4K video at 60fps and outputting RAW 50MP stills, all the while ensuring everything remains smooth and stable, thanks to its three-axis gimbal.</p><p>Accompanied by great AI subject tracking and class-leading flight time of up to 50 minutes, the Atom 3 feels like a competent and powerful content creation tool. The new RC PTD2 controller makes flying a walk in the park, too. But it isn’t perfect. It doesn’t feature any obstacle avoidance, and low-light performance in AI Night Mode can be described as mediocre at best.</p><p>To find out if this is the <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">best drone</a> for you, read my full Potensic Atom 3 review.</p><h2 class="article-body__section" id="section-potensic-atom-3-review-specs"><span>Potensic Atom 3 review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p>Starts at<a href="https://www.amazon.com/Potensic-Display-120-Min-Transmission-QuickShots/dp/B0H14KR92T/" target="_blank" rel="nofollow"> $429</a> /<a href="https://www.amazon.co.uk/Potensic-Standard-Transmission-QuickShots-Battery/dp/B0H11CVQXZ/" target="_blank" rel="nofollow"> £339</a></p></td></tr><tr><td class="firstcol " ><p><strong>Flight time</strong></p></td><td  ><p>40 minutes (Smart Battery) | 50 minutes (Smart Battery Plus)</p></td></tr><tr><td class="firstcol " ><p><strong>FAA/CAA registration</strong></p></td><td  ><p>Not required</p></td></tr><tr><td class="firstcol " ><p><strong>Cameras</strong></p></td><td  ><p>One</p></td></tr><tr><td class="firstcol " ><p><strong>Image sensor</strong></p></td><td  ><p>1/1.3-inch CMOS, 12MP/50MP stills</p></td></tr><tr><td class="firstcol " ><p><strong>ISO</strong></p></td><td  ><p>100-6400 (normal) | 100-25,600 (AI Night)</p></td></tr><tr><td class="firstcol " ><p><strong>Shutter speed</strong></p></td><td  ><p>1/24s to 1/25,000s</p></td></tr><tr><td class="firstcol " ><p><strong>Aperture</strong></p></td><td  ><p>f/2.8</p></td></tr><tr><td class="firstcol " ><p><strong>Focus</strong></p></td><td  ><p>2m to ∞</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>4K/60fps | 2.7K/60fps | FHD/60fps</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Three-axis brushless gimbal</p></td></tr><tr><td class="firstcol " ><p><strong>Indoor use</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Propellers</strong></p></td><td  ><p>Four</p></td></tr><tr><td class="firstcol " ><p><strong>Max takeoff altitude</strong></p></td><td  ><p>5000m</p></td></tr><tr><td class="firstcol " ><p><strong>Max flight distance</strong></p></td><td  ><p>9.94 miles</p></td></tr><tr><td class="firstcol " ><p><strong>Navigation system</strong></p></td><td  ><p>GPS, GLONASS, Galileo, BeiDou</p></td></tr><tr><td class="firstcol " ><p><strong>Collision avoidance</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Smartphone control</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Internal storage</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>microSD card slot</strong></p></td><td  ><p>Yes, V30 or higher</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>9.29 x 6.61 x 2.48 inches (unfolded, without propellers)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>8.78oz / 249g</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-potensic-atom-3-review-price-availability"><span>Potensic Atom 3 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="oyXto6PK49TH3dUcX6RwQS" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/oyXto6PK49TH3dUcX6RwQS.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 Potensic Atom 3 is the fourth drone to join the Chinese brand’s growing lineup of quadcopters. Announced in June 2026, it succeeds the <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-2-review">Potensic Atom 2</a> and receives a $100 price bump, retailing for <a href="https://www.amazon.com/Potensic-Display-120-Min-Transmission-QuickShots/dp/B0H14KR92T/" target="_blank" rel="nofollow">$429</a> /<a href="https://www.amazon.co.uk/Potensic-Standard-Transmission-QuickShots-Battery/dp/B0H11CVQXZ/" target="_blank" rel="nofollow"> £339 at Amazon</a>.</p><p>Unfortunately, the Atom 3 is the latest victim of the <a href="https://www.tomsguide.com/cameras-photography/drones/us-drone-ban-update">ongoing ban on foreign-made drones in the U.S.</a> It isn’t available to purchase officially through Potensic’s website, and, at the time of writing, there’s no news on whether it will receive F.C.C. clearance. You can, however, buy it through Amazon and other third-party sellers. We’ve also seen this happen with DJI’s latest drones, like the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-neo-2-review">DJI Neo 2</a> ($319) which can’t be bought officially through the manufacturer but can be through third-party retailers.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="8gDbyiyiMLS5Z4yPQnFYmQ" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/8gDbyiyiMLS5Z4yPQnFYmQ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 because Potensic hasn’t been cleared to support the Atom 3 in the U.S., you can’t get aftercare packages or repairs should your drone get damaged. I’ve reached out to Potensic’s marketing team to find out about U.S. availability and will update this review as and when I know more.</p><p>The Atom 3 is a strong alternative to the <a href="https://www.amazon.co.uk/DJI-Beginner-Friendly-ActiveTrack-Lightweight-Transmission/dp/B0FZ9XSKKD/" target="_blank" rel="nofollow">DJI Lito X1</a> (£369) as it boasts similar specs packaged into a sub-249g body. Other competitors include the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-flip-review">DJI Flip</a> ($439) which also features a 1/1.3-inch sensor, and the <a href="https://www.amazon.com/DJI-Stabilization-Transmission-Resistance-Battery/dp/B0CXJDDJ9X/" target="_blank" rel="nofollow">DJI Mini 4K</a> ($299) but this drone is over two years old, making the Atom 3 the more up-to-date option.</p><h2 class="article-body__section" id="section-potensic-atom-3-review-design"><span>Potensic Atom 3 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="JSgyDygZBBcgUFTtaBTeiQ" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/JSgyDygZBBcgUFTtaBTeiQ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 put the Potensic Atom 3 and the Potensic Atom 2 side-by-side, you wouldn’t be able to tell the difference between the two — at first glance, that is. Look closer and the differences become obvious. The Atom 3 is slightly bigger, measuring 9.29 x 6.61 x 2.48 inches when unfolded and without propellers, and it’s ever-so slightly heavier too. The Atom 3 weighs 8.78oz as opposed to its predecessor weighing 8.64oz. Its light weight means you don’t necessarily need to register it with F.C.C. in the U.S. or the C.A.A. in the U.K.</p><p>Potensic has made some small but substantial improvements to the design too. The Atom 3 feels more robust and like it’s built much better than the Atom 2. It doesn’t feel too plasticky, and it’s noticeably nicer to touch. In addition to that, the Atom 3’s propellers are larger than the Atom 2’s, which means the drone can push more air, resulting in more efficient operation.</p><p>The ports on the Atom 3 also feel more accessible. On the Atom 2, the battery eject button, annoyingly, covered the microSD card slot, so you had to remove the battery to then remove the microSD card. Not anymore. On the Atom 3, the USB-C port for charging and data transfer, and the microSD card slot sit side-by-side, unobstructed, giving you easy access to both.</p><h2 class="article-body__section" id="section-potensic-atom-3-review-controls"><span>Potensic Atom 3 review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hvdPPvJevrQBBVfr5TUVnQ" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/hvdPPvJevrQBBVfr5TUVnQ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 Potensic Atom 3 hosts just one button on its body: the power button, and it’s much better than the one on the Atom 2. The Atom 2 needed to be powered on and off by pressing the button on the drone’s underside. This meant that you needed to turn it on and then set it on the takeoff surface. On the Atom 3, the button is now located on the top which saves you a few seconds.</p><p>Alongside the Atom 3, Potensic has also unveiled its new controller: the RC PTD2. While you can still get the old controller which requires you to slot in your smartphone and use said phone as the controller’s screen, I’d recommend grabbing the <a href="https://www.amazon.com/Potensic-Display-120-Min-Transmission-QuickShots/dp/B0H14KVJPY/" target="_blank" rel="nofollow">RC PTD2 controller </a>instead.</p><p>The RC PTD2 is sturdier and better built, and its 5.5-inch FHD touchscreen with 900 nits of brightness make flying more immersive and much easier. I’ve found the touchscreen extremely responsive in my testing, and I also like how bright it gets. I haven’t had any issues viewing it in direct sunlight and low-light environments.</p><h2 class="article-body__section" id="section-potensic-atom-3-review-features"><span>Potensic Atom 3 review: Features</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ERfWRQwzkyeYvhh4CcL7AS" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/ERfWRQwzkyeYvhh4CcL7AS.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 Potensic Atom 3 has received meaningful updates to its sensor, making it one of the best beginner drones you can buy today. The drone has a maximum flight distance of 9.94 miles — up from 6.21 miles on the Atom 2. It also has a maximum takeoff altitude of 5,000m which is up from 4,000m on the Atom 2.</p><p>It’s important to note, though, that the Atom 3 doesn’t feature any built-in storage, just like its predecessor. This is disappointing given that the DJI Lito X1 boasts 42GB of internal storage, acting as a safety net if you forget to carry a microSD card with you. Potensic recommends using a V30 (or higher) microSD card with the Atom 3, like the Lexar E-Series 128GB (<a href="https://www.amazon.com/Lexar-MicroSDXC-Memory-Adapter-Tablet/dp/B09XR2MTHK/" target="_blank" rel="nofollow">two for $64</a>).</p><h2 id="sensor">Sensor</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AyPDkrPtFasVzgpeZMAtpQ" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/AyPDkrPtFasVzgpeZMAtpQ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>One of the key differences between the Atom 3 and the Atom 2 is the sensor. The Atom 3 features a larger 1/1.3-inch sensor, as opposed to the Atom 2’s 1/2-inch CMOS sensor. It’s the same sensor you’ll find on the DJI Flip. It’s smaller than the 1-inch sensor on the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-mini-5-pro-review">DJI Mini 5 Pro</a> (£689) but this drone also costs a lot more.</p><p>The aperture, however, has narrowed from f/1.8 on the Atom 2 to f/2.8 on the Atom 3 which, in theory, could mean a detrimental impact on low-light performance, and I’ll discuss this in detail shortly.</p><p>Improvements have been made to the maximum shutter speed, as the Atom 3 can be manually adjusted to shoot at 1/25,000s, as opposed to just 1/6,400s on the Atom 2. You can now capture blur-free footage when tracking fast-moving subjects, like cars or motorbikes.</p><h2 id="wind-resistance">Wind resistance</h2><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="KhbYYckf4bDhiqo5o36meL" name="Potensic_Atom_3-2" alt="Potensic Atom 3 in the air" src="https://cdn.mos.cms.futurecdn.net/KhbYYckf4bDhiqo5o36meL.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>The Atom 3 retains its predecessor’s three-axis brushless gimbal design which helps the drone capture stable and smooth footage, even in mildly windy conditions. A three-axis gimbal is better than a two-axis one (like on the DJI Neo 2) and far more stable than a single-axis one (like on the OG <a href="https://www.tomsguide.com/cameras-photography/drones/dji-neo-review">DJI Neo</a>).</p><p>Wind resistance remains the same across Potensic’s Atom lineup. The Atom 3 can withstand winds up to 10.7 m/s or 23.9mph (level 5). This is common in this price range, as it’s the same wind resistance you’ll find on the DJI Flip and the DJI Mini 4 Pro. If you want even better resistance, the DJI Mini 5 Pro is rated at 12 m/s or 26.84mph.</p><p>I mostly filmed on windless days as the U.K., where I’m based, was experiencing a heatwave during testing, but I was greeted to a few strong gusts every now and then. The Atom 3 remained stable throughout, and I even noticed that its heftier weight made it more resistant to getting knocked about.</p><h2 id="obstacle-avoidance">Obstacle avoidance</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qtCuNnmzpWPNhB5yS9DSZS" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/qtCuNnmzpWPNhB5yS9DSZS.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Potensic still hasn’t fixed my biggest grievance with the Atom 2: the lack of obstacle avoidance. The Atom 3 doesn’t feature any form of collision avoidance which means you have to rely on your eyes and the controller’s screen to ensure the drone doesn’t run into anything.</p><p>While this wasn’t abnormal for the Atom 2’s price range, it’s disappointing given that the Atom 3 costs more. Even though the DJI Flip doesn’t feature omnidirectional obstacle avoidance, it at least features forward-facing avoidance, giving you some reassurance. The DJI Lito X1 takes things further with omnidirectional obstacle sensing. Heck, even the cheaper DJI Neo 2 has omnidirectional avoidance! What gives, Potensic?</p><h2 class="article-body__section" id="section-potensic-atom-3-review-video-quality"><span>Potensic Atom 3 review: Video quality</span></h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/TicKrUWazKg" allowfullscreen></iframe></div></div><p>The Potensic Atom 3 doesn’t give you much to complain about when it comes to video work. It's capable of recording 4K video at 60fps (up from 30fps on the Atom 2). You also have the option to shoot in FHD at 60fps or 2.7K video (at 60fps again) in the 9x16 format for social media. In the video above, you can see a compilation of clips shot in 4K at 60fps. The movement is smooth and stable, even when I’m panning, and the colors look lovely. Greens are vivid while the blue sky appears rich.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/r5tVjX0oOHA" allowfullscreen></iframe></div></div><p>Similar to the Atom 2, the Atom 3 can shoot in P-Log too, which preserves flat, raw footage for increased dynamic range, although you’ll need to grade the footage in post.. You can see what the unedited footage looks like in the video above.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/T4UC9rfvZcQ" allowfullscreen></iframe></div></div><p>The Atom 3 utilizes AI Tracking 2.0 to power its QuickShot short-form modes, such as follow, rocket, spiral, parallel tracking, and more. These modes use AI subject detection to lock onto a subject, and they work very well. They’re especially fun when you’re with a group of friends, and they’re great for creating content quickly for sharing on social media.</p><p>You can see a few examples of the QuickShot modes in the video above. To track a subject, all you need to do is draw a box around the building or person using the controller’s screen, and you’re good to go.</p><div class="looped-video"><video class="lazyload-in-view lazyloading" data-src="https://cdn.mos.cms.futurecdn.net/jrijHKeBafKvbofsJ4aYB3/Potensic%20Atom%203%20%E2%80%94%20Digital%20zoom.mp4" autoplay loop muted playsinline src="https://cdn.mos.cms.futurecdn.net/jrijHKeBafKvbofsJ4aYB3/Potensic%20Atom%203%20%E2%80%94%20Digital%20zoom.mp4"></video></div><p>For getting close to subjects without physically moving your drone, the Atom 3 features a 2x digital zoom at 4K resolution, 3x digital zoom at 2.7K, and 4x at 1080P. Above, you can see what the 2x digital zoom looks like. The quality isn’t the best, as there’s a significant drop in the building’s sharpness. This is a limitation posed by the DJI Flip too, which features the same sensor size, so it isn’t a rare occurrence in this price range.</p><div class="looped-video"><video class="lazyload-in-view lazyloading" data-src="https://cdn.mos.cms.futurecdn.net/XTRysrnuZZTMKgukpwjYFe/Potensic%20Atom%203%20%E2%80%94%20Vertical%20video.mp4" autoplay loop muted playsinline src="https://cdn.mos.cms.futurecdn.net/XTRysrnuZZTMKgukpwjYFe/Potensic%20Atom%203%20%E2%80%94%20Vertical%20video.mp4"></video></div><p>It’s important to note that the vertical video Atom 3 shoots isn’t <em>true</em> vertical — like on the DJI Mini 5 Pro where the gimbal rotates at a 90° angle. The Atom 3 captures standard horizontal footage and then digitally crops it to achieve a 9x16 aspect ratio. That’s no bad thing, though, and vertical footage looks nice and detailed enough for viewing on smartphones.</p><p>While the Atom 3 is capable of producing lovely footage during daytime, especially in sunny conditions, it falters in unfavorable lighting conditions — especially at night. The Atom 3 has a narrow f/2.8 aperture but a large 1/1.3-inch sensor. It’s also got an AI Night Mode which uses AI image processing to enhance visuals “without overexposure.”</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/Qm6vdttfQBo" allowfullscreen></iframe></div></div><p>There are a couple of reasons why I wouldn’t recommend flying the Atom 3 at night. Firstly, the low-light performance is quite poor. Watch the video above. The first clip was shot in normal mode while the second was captured with AI Night Mode active. I personally prefer the first clip as it looks more natural and the motion while panning or when cars are zooming past doesn’t appear jarring.</p><p>The AI Night Mode, on the other hand, makes the video look a little fake as it has added a strange blue hue to the sky by essentially increasing the exposure. There’s a lot of noise and grain in the dark areas, and while panning, you can see a fair amount of motion blur. That’s also because the Atom 3 drops the 4K frame rate down to just 25fps in AI Night Mode from 60fps in regular mode.</p><p>Secondly, because the Atom 3 doesn’t feature any form of obstacle avoidance, it’s difficult to fly the drone at night when you can’t always see it. For those two reasons, if you plan on using a drone for low-light videography, I’d recommend the DJI Flip with its f/1.7 aperture and life-like low-light capabilities. Even better is the DJI Mini 5 Pro, which is worth the extra spend for its large 1-inch f/1.8 sensor alone.</p><h2 class="article-body__section" id="section-potensic-atom-3-review-image-quality"><span>Potensic Atom 3 review: Image quality</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/8aMpwov3BMrykMkHpV4Gg5.jpg" alt="An image taken on the Potensic Atom 3 drone" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/KoxJsLZceoYcXEVXz3wUT6.jpg" alt="An image taken on the Potensic Atom 3 drone" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Z6Lp7bmEeqw3igMPNufYC6.jpg" alt="An image taken on the Potensic Atom 3 drone" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pzPVrVqTVoFgcE4XxVXPG6.jpg" alt="An image taken on the Potensic Atom 3 drone" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MzAWcLHS5zHHsbmCaHutZ6.jpg" alt="An image taken on the Potensic Atom 3 drone" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The Potensic Atom 3 is capable of capturing 12MP images with its 1/1.3-inch sensor. For the first time on a Potensic drone, the Atom 3 can capture RAW photos too for more freedom in post-production. The photos in the gallery above are straight-out-of-camera JPEGs and as you can see, color reproduction is faithful and true to life. I especially love how the sunlight falls on the landscape in the first two slides.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/yX5CTM3pdKHud595UrFda7.jpg" alt="An image taken on the Potensic Atom 3 drone" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/yNMdZR6vsDJV3yVaMJc3c6.jpg" alt="An image taken on the Potensic Atom 3 drone" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>If you want to capture more high-res images, the Atom 3 can take 50MP stills with a resolution of 8192 x 6144 in what Potensic calls it the drone’s 8K mode. The images in the gallery above were taken in the 8K mode and as you can see, they’re packed with detail. The colors are nothing short of stunning, and the buildings in the first shot especially are sharp and in focus.</p><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="u5tdwCafV2TnmfYZsPZwt5" name="Potensic Atom 3_sample_14.JPG" alt="An image taken on the Potensic Atom 3 drone" src="https://cdn.mos.cms.futurecdn.net/u5tdwCafV2TnmfYZsPZwt5.jpg" mos="" align="middle" fullscreen="1" width="4096" height="2304" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/u5tdwCafV2TnmfYZsPZwt5.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: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>Just like you do when shooting video, you get access to a 2x digital zoom when capturing 12MP stills. But similar to the zoomed-in video earlier, the Atom 3 struggles to reproduce sharp stills when the drone is zoomed in to its limits. The photo above was taken with the 2x digital zoom active and as you can see, the building looks fuzzy and low-res. 12MP doesn’t provide a ton of wiggle room either, and no digital zoom is available in the aforementioned 8K mode.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/3zhjKJj2FWKR4kpGuc8TP6.jpg" alt="An image taken on the Potensic Atom 3 drone" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/qeCVTBxYmJ9F8t4Ncosmm5.jpg" alt="An image taken on the Potensic Atom 3 drone" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The Atom 3’s low-light performance when taking stills in dimly lit environments is decent too, as you can see in the gallery above. No AI Night Mode here, and I like that the images are fairly noise-free. I love the look of the sunlight peeking through the clouds as the sun sets over Bristol, U.K. </p><h2 class="article-body__section" id="section-potensic-atom-3-review-app"><span>Potensic Atom 3 review: App</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/LU2hUpyz9qGsCs2Ut9Stxm.jpg" alt="Potensic Eve app for Potensic Atom 3 drone" /><figcaption><small role="credit">Potensic / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/XAfuoRZis97kFvguqRXujm.jpg" alt="Potensic Eve app for Potensic Atom 3 drone" /><figcaption><small role="credit">Potensic / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Sw29uTRyiPuhw4aNS6edim.jpg" alt="Potensic Eve app for Potensic Atom 3 drone" /><figcaption><small role="credit">Potensic / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fpzexBXqKhkWPniC5wnJnm.jpg" alt="Potensic Eve app for Potensic Atom 3 drone" /><figcaption><small role="credit">Potensic / Tom's Guide</small></figcaption></figure></figure><p>The Potensic Atom 3 is accompanied by the Potensic Eve app, available for free on <a href="https://apps.apple.com/us/app/potensic-eve/id6478535830" target="_blank" rel="nofollow">iOS</a> and <a href="https://play.google.com/store/apps/details?id=com.ipotensic.atom&hl=en_GB" target="_blank" rel="nofollow">Android</a>. I thoroughly enjoyed using the app when it was first introduced with the Atom 2, so I’m happy to see it make a return. It sports a similar interface to the excellent DJI Fly app, so if you’re familiar with it or even if this is your first time using a drone app, you’ll have no issues getting to grips with it.</p><p>If you’re using the standard mobile controller with the Atom 3, you’ll be heavily relying on Potensic Eve. In addition to acting as your eyes, the app allows you to see low-res previews of videos and stills you’ve captured, update the drone’s firmware, view flight logs, and access Potensic Academy for tutorials and how-tos.</p><h2 class="article-body__section" id="section-potensic-atom-3-review-battery-life"><span>Potensic Atom 3 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="y66VDzQ5vCVLR7tZAEAkMS" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/y66VDzQ5vCVLR7tZAEAkMS.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 Potensic Atom 2’s flight time was already respectable, and the Potensic Atom 3 one-ups it with longer battery life. Using the standard Smart Battery, the Atom 3 is rated for 40 minutes, and this jumps up to 50 minutes with the Smart Battery Plus. Based on these figures alone, the Atom 3 trumps its competitors. For instance, the DJI Lito X1 is rated for 36 minutes while the DJI Flip can last just 31 minutes between charges or battery swaps.</p><p>Your mileage will naturally depend on the shooting settings and flying conditions. Flying in mostly windy conditions, I was able to squeeze 35 minutes out of the Atom 3’s Smart Battery — but I was also switching between modes, viewing the gallery, etc. between shots. It’s worth investing in one of the Fly More combos — either the <a href="https://www.amazon.com/Potensic-Display-120-Min-Transmission-QuickShots/dp/B0H14KVJPY/" target="_blank" rel="nofollow">RC PTD2 bundle ($679)</a> or the standard <a href="https://www.amazon.com/Potensic-Display-120-Min-Transmission-QuickShots/dp/B0H14PH8Z9/" target="_blank" rel="nofollow">Fly More bundle ($549)</a> — if you want two additional batteries.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Jpeo3NsZaEzLZzHSHASzFS" name="Potensic_Atom_3" alt="Potensic Atom 3 4K drone" src="https://cdn.mos.cms.futurecdn.net/Jpeo3NsZaEzLZzHSHASzFS.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Unlike the Atom 2 whose charging hub used a proprietary cable for charging, the Atom 3’s hub sports a USB-C hub — now that’s more like it! The parallel charging hub charges all three batteries simultaneously in just under 90 minutes. If you’re using Smart Battery Plus, these require 135 minutes to go from zero to 100%.</p><h2 class="article-body__section" id="section-potensic-atom-3-review-verdict"><span>Potensic Atom 3 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="2EbJoewwzUzUwwqxhEbVgL" name="Potensic_Atom_3" alt="Potensic Atom 3 in the air" src="https://cdn.mos.cms.futurecdn.net/2EbJoewwzUzUwwqxhEbVgL.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>If you’re taking your first steps into the world of aerial photography, or are stepping up from a budget drone, the Potensic Atom 3 is an excellent choice. Easy to fly thanks to the new RC PTD2 controller, the Atom 3 is compact and lightweight, making it great for travel too. It captures 50MP stills in JPEG and RAW, giving you more freedom in post-production.</p><p>4K/60fps video is stunning and stable, thanks to the drone’s three-axis gimbal. AI subject tracking works excellently too, and the Atom 3’s QuickShot modes work well and are superb for creating short-form content for quick sharing to social media. However, the Atom 3 doesn’t feature any collision avoidance sensors and its low-light performance is far from best, so I wouldn’t recommend getting the drone solely for shooting at nighttime. Those drawbacks alone make the DJI Flip and the DJI Lito X1 more enticing.</p><p>For everything else, though? It’s almost perfect. The Atom 3 improves on its predecessor to deliver one of the best beginner drones yet.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ These 9 Kodak instant cameras and accessories are must-haves for your summer break — starting at $9.99 ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/these-9-kodak-instant-cameras-and-accessories-are-must-haves-for-your-summer-break-starting-at-usd9-99</link>
                                                                            <description>
                            <![CDATA[ Kodak instant cameras are a great way to preserve your cherished memories. We've rounded up some of our favorites, plus printers and other accessories. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">GT8Cgv2jLZAhv9JBsShAWX</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ByUDJQDRvUZERbNHz6CjHb-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 29 Jun 2026 16:31:33 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></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/ByUDJQDRvUZERbNHz6CjHb-1280-80.jpg">
                                                            <media:credit><![CDATA[Kodak / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of Kodak cameras and printers on a blue background.]]></media:description>                                                            <media:text><![CDATA[A selection of Kodak cameras and printers on a blue background.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of Kodak cameras and printers on a blue background.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ByUDJQDRvUZERbNHz6CjHb-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Taking snaps on your phone is great, but there's nothing quite like using an instant camera for that tactile, immersive feeling. They're a brilliant way to capture memories while you're on holiday or out with friends. Now that we're well into summer — with its vacations, parties, and fun days out — I decided to round up some of our favorites from one of the most iconic brands in all of photography.</p><p>Kodak has been making great photographic film for more than 100 years, and the company has weathered huge changes in the industry. Over the last couple of decades, the brand has developed some superb instant cameras that are just the thing for taking on holiday, popping in a bag on a night out, or simply carrying around to catch memorable everyday moments.</p><p>With all that in mind, I've gathered a list of some of my favorite Kodak instant cameras, printers, and peripherals. These budget-friendly models are ideal for beginners and a great way to preserve the holiday vibes this summer.</p>        <div class="featured_product_block featured_block_hero" data-id="706f4e17-81ba-4212-9954-a5300d1d37ca">            <a href="https://www.amazon.com/dp/B0BRL6LSDX" data-model-name="Kodak Magnifying Lighted Slide Viewer" 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/7nFt3w9ekyqCvfxr2WYaKJ.jpg" alt="Kodak Magnifying Lighted Slide Viewer, Compact Portable 1.8x Led Backlight for Vintage 35mm Film Slides, Battery Operated, Neck Lanyard Included"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Kodak Magnifying Lighted Slide Viewer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="eb179027-5dca-4414-b2ff-d550194192e8">            <a href="https://www.amazon.com/dp/B09TQSVDGW" data-model-name="Kodak LED Light Box" 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/fpvXSo3gN45MQ5vR6SCgfH.jpg" alt="Kodak Led Light Box - 5 X 4 In, Usb & Battery Powered - 98% Color-Accurate Product Photography - Dual Usb & Battery Power for Light Pad - Glare-Free Led for Drawing Board & Tracing"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Kodak LED Light Box</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="633b7e20-4c58-4b9a-8f3e-dc524e1accda">            <a href="https://www.amazon.com/dp/B08FSXD12D" data-model-name="Kodak Mini 2 Retro 2x3" Portable Photo Printer" 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/qLfEFJZCuiJT4fqCv2eaMH.jpg" alt="Kodak Mini 2 Retro 2x3" Portable Photo Printer, 38 Sheets, Bluetooth Smartphone Printer for Iphone & Android, Instant Color Prints, 4pass Dye Sublimation, Black"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Kodak Mini 2 Retro 2x3" Portable Photo Printer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="23383f3f-0e9c-4c62-a7eb-9d31de8417cd">            <a href="https://www.amazon.com/dp/B07BB5FDS2" data-model-name="Kodak Printomatic Full-Color Instant Print Digital Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/YuEwwpf3duGeJ4rPUqnkhG.jpg" alt="Kodak Printomatic Full-Color Instant Print Digital Camera, Zink 2x3" (5.1x7.5 Cm) Sticky-Back Photo Paper, 5mp, Portable, Creative Fun Gift for Birthday, Christmas, Holiday, Blue"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Kodak Printomatic Full-Color Instant Print Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="8493967c-824a-4221-ac81-c282fa88148b">            <a href="https://www.amazon.com/dp/B08FDCNBTF" data-model-name="Kodak Dock Plus 4x6" Photo Printer" 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/3zthz4dqebAh2M5aw7gfzG.jpg" alt="Kodak Dock Plus 4x6'' Photo Printer, 10 Sheets, Docking & Bluetooth Smartphone Printer for Iphone & Android, Instant Color Prints, 4pass Dye Sublimation"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Kodak Dock Plus 4x6" Photo Printer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e0a25077-7478-49f1-8985-aca518939664">            <a href="https://www.amazon.com/dp/B088PQR3YL" data-model-name="Kodak Mini Shot 2 Retro 2x3" Instant Digital Camera & Photo Printer" 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/JeNv7q32DrDF4uWw2gnqbU.jpg" alt="A Kodak Mini Shot."></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Kodak Mini Shot 2 Retro 2x3" Instant Digital Camera & Photo Printer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="493192ee-a18e-4d3b-a480-e772c562594f">            <a href="https://www.amazon.com/dp/B08HCPRN88" data-model-name="Kodak Mini Shot 3 Retro 3x3" Instant Digital Camera & Photo Printer" 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/6YtVkBpZLxzj49TTPvMCDG.jpg" alt="Kodak Mini Shot 3 Retro 3x3” Instant Digital Camera & Photo Printer, 38 Sheets, 2-In-1 Portable Print Camera, Bluetooth Smartphone Printer for Ios & Android, 4pass Dye Sublimation, White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Kodak Mini Shot 3 Retro 3x3" Instant Digital Camera & Photo Printer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="36ef2220-7463-4c55-888a-e26d9e8eca9e">            <a href="https://www.amazon.com/dp/B0B3F6KJJ2" data-model-name="Kodak Slide N Scan Max Digital Film Scanner" 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/Fn5MqiKCdwccP94X9mW4yH.jpg" alt="Kodak Slide N Scan Max Digital Film Scanner - Black/yellow - Tiltable 7-Inch Lcd With Gallery Mode & Photo Scanner Preview - Quick-Feed Tray for Slide Digitizer"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Kodak Slide N Scan Max Digital Film Scanner</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="f90e0b23-4e95-4b1e-91dc-d4b26976725f">            <a href="https://www.amazon.com/dp/B0B786237W" data-model-name="Kodak Reels 8mm & Super 8 Film Digitizer" 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/Zc2SbbqFKFANZyTerFxQqJ.jpg" alt="Kodak Reels 8mm & Super 8 Films Digitizer Converter With Big 5” Screen, Scanner Converts Film Frame by Frame to Digital Mp4 Files for Viewing, Sharing & Saving on Sd Card for 3” 4” 5” 7” and 9” Reels"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Kodak Reels 8mm & Super 8 Film Digitizer</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The DJI Osmo Pocket 4 is already getting an update — I spent 3 days with the Osmo Pocket 4P in Malta and here's what I'm allowed to tell you (with sample footage!) ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4p-first-impressions</link>
                                                                            <description>
                            <![CDATA[ I spent three days recording the sights and sounds of Malta on the DJI Osmo Pocket 4P, and the footage I captured is nothing short of beautiful. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">6HGJWCDsgxAkUzPqiVfKRB</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/maeyz6sjXJLSFfBVDYQRfE-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 29 Jun 2026 10:41:41 +0000</pubDate>                                                                                                                                <updated>Mon, 29 Jun 2026 13:10:52 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/maeyz6sjXJLSFfBVDYQRfE-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A black DJI Osmo Pocket 4P gimbal camera]]></media:description>                                                            <media:text><![CDATA[A black DJI Osmo Pocket 4P gimbal camera]]></media:text>
                                <media:title type="plain"><![CDATA[A black DJI Osmo Pocket 4P gimbal camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/maeyz6sjXJLSFfBVDYQRfE-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The cat's out of the bag. After months of rumors and online speculation, DJI has finally unveiled the dual-camera sibling to the 5-star <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review">Osmo Pocket 4</a>. The DJI Osmo Pocket 4P is here and it's very real — I've held it, shot footage on it, and I can tell you that it's a marvellous little camera.</p><p>I've just got back from spending a few days in Malta, where I recorded the sights and sounds of the Southern European country on the Osmo Pocket 4P. For those of you who may already have heard rumblings on the internet, you'll know that the Osmo Pocket 4P is designed to go toe-to-toe with the <a href="https://www.adorama.com/insta360-luna-ultra-8k-pocket-gimbal-camera-standard-combo-black/p/instaluna01" target="_blank" rel="nofollow">Insta360 Luna Ultra</a> — Insta360's flagship dual-lens gimbal camera.</p><p>The Luna Ultra is already available to buy in the U.S. which gives it an upper-hand over the Osmo Pocket 4P. It's been a few months since the Osmo Pocket 4 came out and it still can't be bought in the U.S. <a href="https://www.tomsguide.com/cameras-photography/drones/us-drone-ban-update">due to the ongoing restrictions</a> and tariffs on the brand's products by the U.S. government — so it's unlikely the Osmo Pocket 4P will become available across the pond at the same time as in Europe.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:7092px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="yFbdbpvn3VycBTwrRfvzMR" name="DJI Osmo Pocket 4P-3" alt="DJI Osmo Pocket 4P gimbal camera" src="https://cdn.mos.cms.futurecdn.net/yFbdbpvn3VycBTwrRfvzMR.jpg" mos="" align="middle" fullscreen="" width="7092" height="3989" 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>Should the camera become available in the U.S., though, it's certainly worth buying. I can't tell you much about the camera's specs but I <em>can</em> confirm that the Osmo Pocket 4P has two lenses, one of which is a 60mm medium-telephoto.</p><p>I can't discuss resolution or frame rates, but I can tell you that the Osmo Pocket 4P features D-Log2 support as well as a whopping 17 stops of dynamic range — giving you plenty of wiggle room in post-production. Design-wise, the Osmo Pocket 4P comes in black or white, and the latter is a first for DJI.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:7419px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZvT4LkdiLmzFU46uJTpMEQ" name="DJI Osmo Pocket 4P-6" alt="DJI Osmo Pocket 4P gimbal camera" src="https://cdn.mos.cms.futurecdn.net/ZvT4LkdiLmzFU46uJTpMEQ.jpg" mos="" align="middle" fullscreen="" width="7419" height="4173" 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>We don't know about pricing yet. Given that the Osmo Pocket 4 costs £429 in the U.K., and the Luna Ultra retails for <a href="https://www.adorama.com/insta360-luna-ultra-8k-pocket-gimbal-camera-standard-combo-black/p/instaluna01" target="_blank" rel="nofollow">$769 in the U.S.</a>, maybe the Osmo Pocket 4P would cost between $550 and $650.</p><p>DJI typically undercuts Insta360 (which always prices high) in all but the U.S. market, where tariffs have forced up DJI's pricing. I imagine DJI would want to undercut Insta360 here by pricing the Osmo Pocket 4P cheaper than the Luna Ultra. We've seen it with the <a href="https://www.tomsguide.com/cameras-photography/drones/antigravity-a1-review">Antigravity A1</a> which uses Insta360's 360° imaging tech. It came out long before the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-avata-360-review">DJI Avata 360</a> and it carried an exorbitant price tag ($1,599!). DJI priced its first 360° drone far cheaper than the A1, which made the Avata 360 better value for money. This is all speculation, of course, and we won't know for certain until DJI releases this information.</p><h2 id="the-sights-and-sounds-of-malta-on-the-dji-osmo-pocket-4p">The sights and sounds of Malta on the DJI Osmo Pocket 4P</h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/0RR3ZHn9PIs" allowfullscreen></iframe></div></div><p>Enough talk of specs and rumors. I've shot on the DJI Osmo Pocket 4P, so what does footage look like? Stunning, in one word. Above, I've compiled a vlog of the videos I recorded in Malta so you can feast your eyes. As you can see, stabilization is fantastic; the colors appear true to life and vibrant; and the dynamic range performance is excellent. There's plenty of detail to enjoy in dark shadows, and the highlights don't appear blown out either.</p><p>That's all I can really say, and all I'm allowed to tell you, without talking about specs. We will have a full review of the Osmo Pocket 4P in the near future. In the meantime, what do you think of the video quality and the camera in general? Let me know in the comments below!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ePVM1O"></div>                            </div>                            <script src="https://kwizly.com/embed/ePVM1O.js" async></script><h2 id="what-can-i-buy-right-now-if-not-the-dji-osmo-pocket-4p">What can I buy right now if not the DJI Osmo Pocket 4P?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:8160px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="qCUokiPvBDuDKcMxQ6DiKP" name="DJI Osmo Pocket 4P" alt="DJI Osmo Pocket 4P gimbal camera" src="https://cdn.mos.cms.futurecdn.net/qCUokiPvBDuDKcMxQ6DiKP.jpg" mos="" align="middle" fullscreen="" width="8160" height="4590" 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>Now that you've seen the footage I recorded in Malta, have I enticed you into buying the DJI Osmo Pocket 4P when it comes out? I apologize to my American comrades because I don't know whether the Osmo Pocket 4P will be available in U.S. at launch — but fret not, there are alternatives!</p><div class="product"><a data-dimension112="43ef7d2e-445b-4438-b4ed-f3bfa4ff3e20" data-action="Deal Block" data-label="Even though its successor isn't available in the U.S., the DJI Osmo Pocket 3 is still a fantastic gimbal camera. It's the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage." data-dimension48="Even though its successor isn't available in the U.S., the DJI Osmo Pocket 3 is still a fantastic gimbal camera. It's the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage." data-dimension25="$378" href="https://www.amazon.com/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:425px;"><p class="vanilla-image-block" style="padding-top:252.00%;"><img id="A42QpbMsUGy822dgTrzqi6" name="DJI Osmo Pocket 3" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/A42QpbMsUGy822dgTrzqi6.jpg" mos="" align="middle" fullscreen="" width="425" height="1071" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Even though its successor isn't available in the U.S., the DJI Osmo Pocket 3 is still a fantastic gimbal camera. It's the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage.<a class="view-deal button" href="https://www.amazon.com/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD/" target="_blank" rel="nofollow" data-dimension112="43ef7d2e-445b-4438-b4ed-f3bfa4ff3e20" data-action="Deal Block" data-label="Even though its successor isn't available in the U.S., the DJI Osmo Pocket 3 is still a fantastic gimbal camera. It's the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage." data-dimension48="Even though its successor isn't available in the U.S., the DJI Osmo Pocket 3 is still a fantastic gimbal camera. It's the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage." data-dimension25="$378">View Deal</a></p></div><div class="product"><a data-dimension112="9ddac6d2-5d83-4f18-88d9-63eb9ab718ef" data-action="Deal Block" data-label="The Insta360 Luna Ultra is the DJI Osmo Pocket 4P's closest rival. It packs a Leica lens with a 1-inch sensor, and captures 8K video at 30fps. Its 2-inch OLED touchscreen is detachable and can be used for remote viewing and camera control. The Luna Ultra comes in black and white." data-dimension48="The Insta360 Luna Ultra is the DJI Osmo Pocket 4P's closest rival. It packs a Leica lens with a 1-inch sensor, and captures 8K video at 30fps. Its 2-inch OLED touchscreen is detachable and can be used for remote viewing and camera control. The Luna Ultra comes in black and white." data-dimension25="$769" href="https://www.adorama.com/insta360-luna-ultra-8k-pocket-gimbal-camera-standard-combo-black/p/instaluna01" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:716px;"><p class="vanilla-image-block" style="padding-top:134.08%;"><img id="WoHznAsQer6MkADNwYLnMQ" name="Insta360 Luna Ultra_deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WoHznAsQer6MkADNwYLnMQ.png" mos="" align="middle" fullscreen="" width="716" height="960" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Insta360 Luna Ultra is the DJI Osmo Pocket 4P's closest rival. It packs a Leica lens with a 1-inch sensor, and captures 8K video at 30fps. Its 2-inch OLED touchscreen is detachable and can be used for remote viewing and camera control. The Luna Ultra comes in black and white.<a class="view-deal button" href="https://www.adorama.com/insta360-luna-ultra-8k-pocket-gimbal-camera-standard-combo-black/p/instaluna01" target="_blank" rel="nofollow" data-dimension112="9ddac6d2-5d83-4f18-88d9-63eb9ab718ef" data-action="Deal Block" data-label="The Insta360 Luna Ultra is the DJI Osmo Pocket 4P's closest rival. It packs a Leica lens with a 1-inch sensor, and captures 8K video at 30fps. Its 2-inch OLED touchscreen is detachable and can be used for remote viewing and camera control. The Luna Ultra comes in black and white." data-dimension48="The Insta360 Luna Ultra is the DJI Osmo Pocket 4P's closest rival. It packs a Leica lens with a 1-inch sensor, and captures 8K video at 30fps. Its 2-inch OLED touchscreen is detachable and can be used for remote viewing and camera control. The Luna Ultra comes in black and white." data-dimension25="$769">View Deal</a></p></div><div class="product"><a data-dimension112="49bd29bb-c9e7-4b2e-9bc7-a10b6ba4b1be" data-action="Deal Block" data-label="If you're in the U.K., the 5-star DJI Osmo Pocket 4 is a no-brainer. An upgrade from the Pocket 3 rather than a wholesale redevelopment, the new model nevertheless makes changes in all the right places to build on an already winning recipe. Video looks beautiful, stabilization and autofocus are on point, and there’s a wealth of connectivity options to make creators’ lives easier." data-dimension48="If you're in the U.K., the 5-star DJI Osmo Pocket 4 is a no-brainer. An upgrade from the Pocket 3 rather than a wholesale redevelopment, the new model nevertheless makes changes in all the right places to build on an already winning recipe. Video looks beautiful, stabilization and autofocus are on point, and there’s a wealth of connectivity options to make creators’ lives easier." data-dimension25="£429" href="https://www.amazon.co.uk/DJI-Essential-Stabilization-Lossless-14%E2%80%91Stop/dp/B0FYQ1LFFK/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:532px;"><p class="vanilla-image-block" style="padding-top:281.95%;"><img id="Ps4UtCmeJMgiqcmff4549k" name="Osmo Pocket 4 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Ps4UtCmeJMgiqcmff4549k.jpg" mos="" align="middle" fullscreen="" width="532" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you're in the U.K., the 5-star DJI Osmo Pocket 4 is a no-brainer. An upgrade from the Pocket 3 rather than a wholesale redevelopment, the new model nevertheless makes changes in all the right places to build on an already winning recipe. Video looks beautiful, stabilization and autofocus are on point, and there’s a wealth of connectivity options to make creators’ lives easier.<a class="view-deal button" href="https://www.amazon.co.uk/DJI-Essential-Stabilization-Lossless-14%E2%80%91Stop/dp/B0FYQ1LFFK/" target="_blank" rel="nofollow" data-dimension112="49bd29bb-c9e7-4b2e-9bc7-a10b6ba4b1be" data-action="Deal Block" data-label="If you're in the U.K., the 5-star DJI Osmo Pocket 4 is a no-brainer. An upgrade from the Pocket 3 rather than a wholesale redevelopment, the new model nevertheless makes changes in all the right places to build on an already winning recipe. Video looks beautiful, stabilization and autofocus are on point, and there’s a wealth of connectivity options to make creators’ lives easier." data-dimension48="If you're in the U.K., the 5-star DJI Osmo Pocket 4 is a no-brainer. An upgrade from the Pocket 3 rather than a wholesale redevelopment, the new model nevertheless makes changes in all the right places to build on an already winning recipe. Video looks beautiful, stabilization and autofocus are on point, and there’s a wealth of connectivity options to make creators’ lives easier." data-dimension25="£429">View Deal</a></p></div><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review">I tested DJI’s new Osmo Pocket 4 — this is the only tool I'll recommend to content creators from now on</a></li><li><a href="https://www.tomsguide.com/cameras-photography/insta360s-gimbal-camera-has-just-hit-shelves-and-theres-one-factor-that-has-me-lining-up-to-purchase-one-stat">Insta360's gimbal camera has just hit shelves, and there's one factor that has me lining up to purchase one stat</a></li><li><a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">The best cameras in 2026</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The DJI Flip just hit its lowest price — take to the skies for just $349 this Prime Day ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/the-dji-flip-just-hit-its-lowest-price-take-to-the-skies-for-just-usd349-this-prime-day</link>
                                                                            <description>
                            <![CDATA[ Let your creativity take flight with these best Amazon Prime Day DJI drone deals and DJI action cam bargains. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">dJRZSg8KPzEcc3TL9XtkKe</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/nGUVDTSE7y6oJtz44E3Eq4-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 26 Jun 2026 01:53:53 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ dan.bracaglia@futurenet.com (Dan Bracaglia) ]]></author>                    <dc:creator><![CDATA[ Dan Bracaglia ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/3Ev8EFrheNxPemMWSBaKcK.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;&lt;br&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/nGUVDTSE7y6oJtz44E3Eq4-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A DJI Flip drone]]></media:description>                                                            <media:text><![CDATA[A DJI Flip drone]]></media:text>
                                <media:title type="plain"><![CDATA[A DJI Flip drone]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/nGUVDTSE7y6oJtz44E3Eq4-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I’ve been flying drones recreationally for almost a decade. It’s one of my favorite visual tools for documenting activities like snowboarding and skateboarding; I also love snapping birds-eye-view shots of cool locations. </p><p>Now, you can let your creativity take flight — for less —  thanks to Prime Day savings on the <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">best drones</a> from DJI, no previous flying experience needed. For example, the seriously beginner-friendly DJI Flip is just <a href="https://www.amazon.com/dp/B0DJ1H6KCP?" target="_blank" rel="nofollow">$349 at Amazon</a>, its lowest price ever. And the DJI Mini 3 is just $269 or <a href="https://www.amazon.com/DJI-Mini-Lightweight-Foldable-Intelligent/dp/B0BL3T49JF/?th=1" target="_blank" rel="nofollow">35% off at Amazon</a>.</p><p>Find links to the best DJI drone deals, plus the best DJI action camera deals below. </p><h3 class="article-body__section" id="section-quick-links"><span>Quick links</span></h3><ul><li><strong>DJI Mini 3: </strong><a href="https://www.amazon.com/DJI-Mini-Lightweight-Foldable-Intelligent/dp/B0BL3T49JF/?th=1" target="_blank" rel="nofollow"><strong>was $419 now $269 @ Amazon</strong></a></li><li><strong>DJI Flip: </strong><a href="https://www.amazon.com/dp/B0DJ1H6KCP" target="_blank" rel="nofollow"><strong>was $439 now $349 @ Amazon</strong></a></li><li><strong>DJI Osmo Action 4: </strong><a href="https://www.amazon.com/dp/B0C783YNW7?th=1" target="_blank" rel="nofollow"><strong>was $299 now $179 @ Amazon</strong></a></li><li><strong>DJI Osmo Action 6 Capture More Combo: </strong><a href="https://www.amazon.com/dp/B0GT4JVFPF?th=1" target="_blank" rel="nofollow"><strong>was $476 now $404 @ Amazon</strong></a></li><li><strong>DJI Osmo Pocket 3 Creator Combo: </strong><a href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0CG19FGQ5/?th=1" target="_blank" rel="nofollow"><strong>was $629 now $512 @ Amazon</strong></a></li></ul><h3 class="article-body__section" id="section-best-dji-prime-day-deals"><span>Best DJI Prime Day deals</span></h3><div class="product"><a data-dimension112="9597e554-fb27-43f6-a5f1-a4f5fbca5d7b" data-action="Deal Block" data-label="DJI Mini 3" data-dimension48="DJI Mini 3" data-dimension25="$269" href="https://www.amazon.com/DJI-Mini-Lightweight-Foldable-Intelligent/dp/B0BL3T49JF/?th=1" 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="WqvVHXNrsYwTJeVudjZ5KS" name="DJI Mini 3 deal block.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WqvVHXNrsYwTJeVudjZ5KS.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/reviews/dji-mini-3" data-dimension112="9597e554-fb27-43f6-a5f1-a4f5fbca5d7b" data-action="Deal Block" data-label="DJI Mini 3" data-dimension48="DJI Mini 3" data-dimension25="$269">DJI Mini 3</a> has been around for a little while and isn't the newest drone in the brand's Mini lineup (that'd be the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-mini-5-pro-review">DJI Mini 5 Pro</a>), but with a sales price of just $269, this deal was too good not to share. After all, the Mini 3 boasts super-long flight times, up to 38 minutes, a responsive and agile flight experience, frontal accident avoidance, and the ability to shoot vertical video in 4K. <a class="view-deal button" href="https://www.amazon.com/DJI-Mini-Lightweight-Foldable-Intelligent/dp/B0BL3T49JF/?th=1" target="_blank" rel="nofollow" data-dimension112="9597e554-fb27-43f6-a5f1-a4f5fbca5d7b" data-action="Deal Block" data-label="DJI Mini 3" data-dimension48="DJI Mini 3" data-dimension25="$269">View Deal</a></p></div><div class="product"><a data-dimension112="0f051e58-2fc1-4740-bce3-2aa7a1d83cb2" data-action="Deal Block" data-label="The DJI Flip is the easiest drone to get the hang of, making it great for beginners. Full prop guards and front-facing accident avoidance mean crashes either won't happen or aren't a big deal. Flight time is an impressive 31 minutes; meanwhile, 4K/60p footage is smooth and eye-pleasing while 48MP stills are delightfully detailed." data-dimension48="The DJI Flip is the easiest drone to get the hang of, making it great for beginners. Full prop guards and front-facing accident avoidance mean crashes either won't happen or aren't a big deal. Flight time is an impressive 31 minutes; meanwhile, 4K/60p footage is smooth and eye-pleasing while 48MP stills are delightfully detailed." data-dimension25="$349" href="https://www.amazon.com/dp/B0DJ1H6KCP" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:799px;"><p class="vanilla-image-block" style="padding-top:76.47%;"><img id="GzxQt2zMfrvMGsjJXS9ZdL" name="DJI-Flip-dealbox" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/GzxQt2zMfrvMGsjJXS9ZdL.png" mos="" align="middle" fullscreen="" width="799" height="611" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The DJI Flip is the easiest drone to get the hang of, making it great for beginners. Full prop guards and front-facing accident avoidance mean crashes either won't happen or aren't a big deal. Flight time is an impressive 31 minutes; meanwhile, 4K/60p footage is smooth and eye-pleasing while 48MP stills are delightfully detailed. <a class="view-deal button" href="https://www.amazon.com/dp/B0DJ1H6KCP" target="_blank" rel="nofollow" data-dimension112="0f051e58-2fc1-4740-bce3-2aa7a1d83cb2" data-action="Deal Block" data-label="The DJI Flip is the easiest drone to get the hang of, making it great for beginners. Full prop guards and front-facing accident avoidance mean crashes either won't happen or aren't a big deal. Flight time is an impressive 31 minutes; meanwhile, 4K/60p footage is smooth and eye-pleasing while 48MP stills are delightfully detailed." data-dimension48="The DJI Flip is the easiest drone to get the hang of, making it great for beginners. Full prop guards and front-facing accident avoidance mean crashes either won't happen or aren't a big deal. Flight time is an impressive 31 minutes; meanwhile, 4K/60p footage is smooth and eye-pleasing while 48MP stills are delightfully detailed." data-dimension25="$349">View Deal</a></p></div><div class="product"><a data-dimension112="dfd31414-4f5a-4ef0-8f41-e7567a2ad56e" data-action="Deal Block" data-label="Osmo Action 4" data-dimension48="Osmo Action 4" data-dimension25="$179" href="https://www.amazon.com/dp/B0C783YNW7?th=1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="8RNoiAvEdbtYEJ6r9mu2EA" name="DJI Osmo Action 4" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8RNoiAvEdbtYEJ6r9mu2EA.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/reviews/dji-osmo-action-4" data-dimension112="dfd31414-4f5a-4ef0-8f41-e7567a2ad56e" data-action="Deal Block" data-label="Osmo Action 4" data-dimension48="Osmo Action 4" data-dimension25="$179">Osmo Action 4</a> isn't the newest DJI action camera, but it's still a mighty capable piece of kit for a seriously good price. Video quality is excellent; you can even shoot 4K/120p slow-motion footage. It also has full-color front and rear screens, decent internal audio, tons of mounting options, and 60 feet of water resistance, no case needed.<a class="view-deal button" href="https://www.amazon.com/dp/B0C783YNW7?th=1" target="_blank" rel="nofollow" data-dimension112="dfd31414-4f5a-4ef0-8f41-e7567a2ad56e" data-action="Deal Block" data-label="Osmo Action 4" data-dimension48="Osmo Action 4" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="5bfc5fca-e5b2-4685-937f-9b04f22f5cff" data-action="Deal Block" data-label="DJI Osmo Action 6" data-dimension48="DJI Osmo Action 6" data-dimension25="$404" href="https://www.amazon.com/dp/B0GT4JVFPF?th=1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1430px;"><p class="vanilla-image-block" style="padding-top:83.85%;"><img id="BsiDCToH9T6SoRKtYEgxmY" name="DJI-6-more" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/BsiDCToH9T6SoRKtYEgxmY.jpg" mos="" align="middle" fullscreen="" width="1430" height="1199" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The latest DJI action camera is also on sale. Grab the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-6-review" data-dimension112="5bfc5fca-e5b2-4685-937f-9b04f22f5cff" data-action="Deal Block" data-label="DJI Osmo Action 6" data-dimension48="DJI Osmo Action 6" data-dimension25="$404">DJI Osmo Action 6</a> Capture More Combo $72 off for Prime Week. This includes the camera, two batteries, and a mountable extension rod. Moreover, the camera's 8K video quality is stunning, and image stabilization is class-leading. For hardcore creatives, it even supports interchangeable lenses. <a class="view-deal button" href="https://www.amazon.com/dp/B0GT4JVFPF?th=1" target="_blank" rel="nofollow" data-dimension112="5bfc5fca-e5b2-4685-937f-9b04f22f5cff" data-action="Deal Block" data-label="DJI Osmo Action 6" data-dimension48="DJI Osmo Action 6" data-dimension25="$404">View Deal</a></p></div><div class="product"><a data-dimension112="07615bac-ca7e-4d78-85f6-98859b19c034" data-action="Deal Block" data-label="DJI Osmo Pocket 3" data-dimension48="DJI Osmo Pocket 3" data-dimension25="$512" href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0CG19FGQ5/?th=1" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:749px;"><p class="vanilla-image-block" style="padding-top:111.75%;"><img id="V3Yh26jh4QvatcmnLzvYfQ" name="Screenshot 2026-06-25 at 6.28.36 PM" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/V3Yh26jh4QvatcmnLzvYfQ.png" mos="" align="middle" fullscreen="" width="749" height="837" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/reviews/dji-osmo-pocket-3" data-dimension112="07615bac-ca7e-4d78-85f6-98859b19c034" data-action="Deal Block" data-label="DJI Osmo Pocket 3" data-dimension48="DJI Osmo Pocket 3" data-dimension25="$512">DJI Osmo Pocket 3</a> is easily one of the best hand-held vlogging cameras on the market today, with impressive 4K video, excellent stabilization, and impressive battery performance, all in a simple-to-use package. <a class="view-deal button" href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0CG19FGQ5/?th=1" target="_blank" rel="nofollow" data-dimension112="07615bac-ca7e-4d78-85f6-98859b19c034" data-action="Deal Block" data-label="DJI Osmo Pocket 3" data-dimension48="DJI Osmo Pocket 3" data-dimension25="$512">View Deal</a></p></div><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Cameras+%26+Photography&price=50_100&view_mode=savings_squad&widget_title=Top+Prime+Day+phone+deals%2C+as+picked+by+us&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;">` : ''}                            \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;">` : ''}                          \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[ Prime Day camera discounts — here's how I'd upgrade my kitbag ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/prime-day-camera-discounts-heres-how-id-upgrade-my-kitbag</link>
                                                                            <description>
                            <![CDATA[ Some solid deals on offer, particularly from Canon, GoPro and DJI ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">gwzjxu8D3b3YDnsPrJnkJM</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/LPR3ATXLwgjdCygPp926Jb-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 25 Jun 2026 15:38:21 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></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/LPR3ATXLwgjdCygPp926Jb-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Canon EOS R50 V in hand with a TG Prime Day badge in the bottom right of the image.]]></media:description>                                                            <media:text><![CDATA[Canon EOS R50 V in hand with a TG Prime Day badge in the bottom right of the image.]]></media:text>
                                <media:title type="plain"><![CDATA[Canon EOS R50 V in hand with a TG Prime Day badge in the bottom right of the image.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/LPR3ATXLwgjdCygPp926Jb-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>With the <a href="https://www.tomsguide.com/live/news/anti-prime-day-2026-best-buy-walmart">Prime Day deals</a> now into their third day, it could be a good time to pick up one of the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras</a> at a very respectable price.</p><p>As is often the case with these kinda of sales events, the reductions on pricier mirrorless bodies are, for the most part, pretty poor. Canon is the only major player offering significant discounts, like <a href="https://www.bhphotovideo.com/c/product/1886961-REG/canon_6895c002_eos_r50_v_mirrorless.html" target="_blank" rel="nofollow">$150 off the fantastic Canon EOS R50V at B&H</a>. Lumix and Sony are offering only modest discounts, and typically on older models like the <a href="https://www.bhphotovideo.com/c/product/1394217-REG/sony_ilce_7m3_alpha_a7_iii_mirrorless.html" target="_blank" rel="nofollow">A7 III (currently $1,398 at B&H)</a>, while Nikon and Fuji are true to form and nowhere to be seen.</p><p>Interestingly, though, there are some awesome deals on action and vlogging cameras. My favorite deal is on the <a href="https://www.amazon.com/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FNGK6JZP" target="_blank" rel="nofollow">GoPro MAX2, one of my top 360 cameras, which is currently $200 off at Amazon</a>.</p><p>I've waded through the river of crappy camera deals to find only the ones I'd recommend this Prime Day. Here they are.</p><h2 id="don-t-be-fooled-by-the-dodgy-deals">Don't be fooled by the dodgy deals</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dUcoGmWQXhv8gNwBatn2QE" name="TG_Sony-a6700_LIST.jpg" alt="Sony a6700 camera sitting on desk" src="https://cdn.mos.cms.futurecdn.net/dUcoGmWQXhv8gNwBatn2QE.jpg" mos="" align="middle" fullscreen="" width="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 a lot of deals out there, but not all of them are good. Take this, the <a href="https://www.amazon.com/Sony-Mirrorless-Camera-16-50mm-3-5-5-6/dp/B0CBN8Y77Z" target="_blank" rel="nofollow">Sony a6700, which is currently reduced from $1,599 to $1,399 at Amazon</a>. Sounds great, right?</p><p>Well, let me clue you in on a little price tracking info: the a6700 sat at $1,399 from August 2024 to July 2025, then it got hiked up to $1,500. Back in March, it got hiked up in price again to $1,599. So sure, there are discounts on it right now, but it's only going back down to where it started. </p><p>Or how about the Lumix S5IIX, which is ostensibly <a href="https://www.amazon.com/Panasonic-Mirrorless-Unlimited-Recording-DC-S5M2XWK9/dp/B0G2C9YXCD" target="_blank" rel="nofollow">reduced from $1,919 to $1,797 at Amazon</a>. Well, that got hiked to $1,900 in April so it could look good now. I've seen that camera drop under the $1,500 mark in the past, which is where I'd expect to see it given it's nearly 3 years old.</p><p>There's a lot of that floating about right now. "Dynamic pricing" so to speak. I won't be including that kind of deal here. No sir-ee.</p><h2 class="article-body__section" id="section-quick-list"><span>Quick list</span></h2><ul><li><strong>Instax mini 12 bundle: </strong><a href="https://www.amazon.com/FUJIFILM-INSTAX-MINI-Pink-Bundle/dp/B0FY3LN7J7/" target="_blank" rel="nofollow"><strong>was $105 now $79 @ Amazon</strong></a><strong></strong></li><li><strong>DJI Osmo Action 5 Pro: </strong><a href="https://www.amazon.com/gp/aw/d/B0DS2DF5S5" target="_blank" rel="nofollow"><strong>was $339 now $288 @ Amazon</strong></a></li><li><strong>GoPro MAX2: </strong><a href="https://www.amazon.com/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FNGK6JZP" target="_blank" rel="nofollow"><strong>was $499 now $299 at Amazon</strong></a><strong></strong></li><li><strong>DJI Osmo Pocket 3 Drive bundle: </strong><a href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0GWV45C6C" target="_blank" rel="nofollow"><strong>was $496 now $375 @ Amazon</strong></a><strong></strong></li><li><strong>Canon EOS R50 V: </strong><a href="https://www.bhphotovideo.com/c/product/1886961-REG/canon_6895c002_eos_r50_v_mirrorless.html" target="_blank" rel="nofollow"><strong>was $719 now $569 @ B&H</strong></a><strong></strong></li><li><strong>Sony A7 III: </strong><a href="https://www.bhphotovideo.com/c/product/1394217-REG/sony_ilce_7m3_alpha_a7_iii_mirrorless.html" target="_blank" rel="nofollow"><strong>was $1,998 now $1,398 @ B&H</strong></a><strong></strong></li><li><strong>Lumix S1R II: </strong><a href="https://www.amazon.com/Panasonic-Mirrorless-Featuring-Full-Frame-First-Ever/dp/B0DY21GMBD" target="_blank" rel="nofollow"><strong>was $3,299 now $2,997 @ Amazon</strong></a></li></ul><h2 class="article-body__section" id="section-editor-approved-prime-day-camera-deals"><span>Editor-approved Prime Day camera deals</span></h2><div class="product"><a data-dimension112="aea2537d-9c17-4d14-b73f-9061b6395b38" data-action="Deal Block" data-label="EOS R50 V review" data-dimension48="EOS R50 V review" data-dimension25="$569" href="https://www.bhphotovideo.com/c/product/1886961-REG/canon_6895c002_eos_r50_v_mirrorless.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1095px;"><p class="vanilla-image-block" style="padding-top:60.46%;"><img id="VYsANy4nTNhcpysoSt6eu8" name="Canon-EOS-R50V-dealbox" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/VYsANy4nTNhcpysoSt6eu8.png" mos="" align="middle" fullscreen="" width="1095" height="662" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In our <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r50-v-review" data-dimension112="aea2537d-9c17-4d14-b73f-9061b6395b38" data-action="Deal Block" data-label="EOS R50 V review" data-dimension48="EOS R50 V review" data-dimension25="$569">EOS R50 V review</a>, we gave this little camera 4.5 stars, as it's a fantastic little vlogging tool. And now, it's cheaper than ever at B&H.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1886961-REG/canon_6895c002_eos_r50_v_mirrorless.html" target="_blank" rel="nofollow" data-dimension112="aea2537d-9c17-4d14-b73f-9061b6395b38" data-action="Deal Block" data-label="EOS R50 V review" data-dimension48="EOS R50 V review" data-dimension25="$569">View Deal</a></p></div><div class="product"><a data-dimension112="4d1ca1f4-60f1-4511-855f-c7f6fb8f2a72" data-action="Deal Block" data-label="This isn't the lowest we've seen the A7 III drop — that was to $1,298 at various points over the last couple of years. However, this is still a fair price that's a little below the average value. Don't believe the $1,998 MSRP, though, as it actually tends to hover around the $1,500 mark. I owned this camera, and can testify to how strong of a performer it is." data-dimension48="This isn't the lowest we've seen the A7 III drop — that was to $1,298 at various points over the last couple of years. However, this is still a fair price that's a little below the average value. Don't believe the $1,998 MSRP, though, as it actually tends to hover around the $1,500 mark. I owned this camera, and can testify to how strong of a performer it is." data-dimension25="$1398" href="https://www.bhphotovideo.com/c/product/1394217-REG/sony_ilce_7m3_alpha_a7_iii_mirrorless.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1700px;"><p class="vanilla-image-block" style="padding-top:82.35%;"><img id="7TXfQBH6m9gdhK6WCtoVHG" name="Sony A7 III Deal.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/7TXfQBH6m9gdhK6WCtoVHG.jpg" mos="" align="middle" fullscreen="" width="1700" height="1400" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This isn't the lowest we've seen the A7 III drop — that was to $1,298 at various points over the last couple of years. However, this is still a fair price that's a little below the average value. Don't believe the $1,998 MSRP, though, as it actually tends to hover around the $1,500 mark. I owned this camera, and can testify to how strong of a performer it is.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1394217-REG/sony_ilce_7m3_alpha_a7_iii_mirrorless.html" target="_blank" rel="nofollow" data-dimension112="4d1ca1f4-60f1-4511-855f-c7f6fb8f2a72" data-action="Deal Block" data-label="This isn't the lowest we've seen the A7 III drop — that was to $1,298 at various points over the last couple of years. However, this is still a fair price that's a little below the average value. Don't believe the $1,998 MSRP, though, as it actually tends to hover around the $1,500 mark. I owned this camera, and can testify to how strong of a performer it is." data-dimension48="This isn't the lowest we've seen the A7 III drop — that was to $1,298 at various points over the last couple of years. However, this is still a fair price that's a little below the average value. Don't believe the $1,998 MSRP, though, as it actually tends to hover around the $1,500 mark. I owned this camera, and can testify to how strong of a performer it is." data-dimension25="$1398">View Deal</a></p></div><div class="product"><a data-dimension112="4a5ee66d-31a8-4a18-8141-552cb9cfec2e" data-action="Deal Block" data-label="Paul swapped to it from Sony for video work" data-dimension48="Paul swapped to it from Sony for video work" data-dimension25="$2997.99" href="https://www.amazon.com/Panasonic-Mirrorless-Featuring-Full-Frame-First-Ever/dp/B0DY21GMBD" 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:74.37%;"><img id="ugwAAg9cbaVHkUYt8ekJ8d" name="Lumix S1R II" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ugwAAg9cbaVHkUYt8ekJ8d.jpg" mos="" align="middle" fullscreen="" width="679" height="505" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This is still a fairly new camera, so I was surprised to see it dropping at all. Lumix does tend to be generous with its discounts, though. We loved the S1RII when our producer <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-tested-the-panasonic-lumix-s1r-ii-and-its-almost-the-perfect-hybrid-camera" data-dimension112="4a5ee66d-31a8-4a18-8141-552cb9cfec2e" data-action="Deal Block" data-label="Paul swapped to it from Sony for video work" data-dimension48="Paul swapped to it from Sony for video work" data-dimension25="$2997.99">Paul swapped to it from Sony for video work</a> — it's an outstanding hybrid workhorse, especially if video is the priority.<a class="view-deal button" href="https://www.amazon.com/Panasonic-Mirrorless-Featuring-Full-Frame-First-Ever/dp/B0DY21GMBD" target="_blank" rel="nofollow" data-dimension112="4a5ee66d-31a8-4a18-8141-552cb9cfec2e" data-action="Deal Block" data-label="Paul swapped to it from Sony for video work" data-dimension48="Paul swapped to it from Sony for video work" data-dimension25="$2997.99">View Deal</a></p></div><div class="product"><a data-dimension112="28f58806-a57a-4aab-95e3-8094133dfae1" data-action="Deal Block" data-label="MAX2" data-dimension48="MAX2" data-dimension25="$299" href="https://www.amazon.com/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FNGK6JZP" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1159px;"><p class="vanilla-image-block" style="padding-top:109.84%;"><img id="CZJ2XozEEfGMiunpAjNVFk" name="GoPro MAX2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/CZJ2XozEEfGMiunpAjNVFk.jpg" mos="" align="middle" fullscreen="" width="1159" height="1273" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This is my favorite deal so far this Prime Day. The <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/gopro-max2-review" target="_blank" data-dimension112="28f58806-a57a-4aab-95e3-8094133dfae1" data-action="Deal Block" data-label="MAX2" data-dimension48="MAX2" data-dimension25="$299">MAX2</a> is probably the closest rival to Insta360's X5 in the 360 market. This $200 discount is rather surprising, as the MAX2 is still very new. Unless you're invested in Insta360 or DJI systems already, it's the 360 camera deal I recommend.<a class="view-deal button" href="https://www.amazon.com/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FNGK6JZP" target="_blank" rel="nofollow" data-dimension112="28f58806-a57a-4aab-95e3-8094133dfae1" data-action="Deal Block" data-label="MAX2" data-dimension48="MAX2" data-dimension25="$299">View Deal</a></p></div><div class="product"><a data-dimension112="ca6e6204-6352-48c7-ab1f-ce914e5107b1" data-action="Deal Block" data-label="DJI Osmo Action 5 Pro" data-dimension48="DJI Osmo Action 5 Pro" data-dimension25="$288" href="https://www.amazon.com/gp/aw/d/B0DS2DF5S5" 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:81.33%;"><img id="yuhEUh9SGzYYoDGQX6gPJT" name="DJI Osmo Action 5 Pro GRID" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yuhEUh9SGzYYoDGQX6gPJT.jpg" mos="" align="middle" fullscreen="" width="1500" height="1220" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-5-pro-review" target="_blank" data-dimension112="ca6e6204-6352-48c7-ab1f-ce914e5107b1" data-action="Deal Block" data-label="DJI Osmo Action 5 Pro" data-dimension48="DJI Osmo Action 5 Pro" data-dimension25="$288">DJI Osmo Action 5 Pro</a> held the top spot on my best action cameras list for a decent stretch it got replaced by the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-6-review" target="_blank">Osmo Action 6</a>). Despite that, it's still fantastic, and will give pros and content creators everything they could want from a rugged camera.<a class="view-deal button" href="https://www.amazon.com/gp/aw/d/B0DS2DF5S5" target="_blank" rel="nofollow" data-dimension112="ca6e6204-6352-48c7-ab1f-ce914e5107b1" data-action="Deal Block" data-label="DJI Osmo Action 5 Pro" data-dimension48="DJI Osmo Action 5 Pro" data-dimension25="$288">View Deal</a></p></div><div class="product"><a data-dimension112="27e6f0ee-d2a7-49ab-968f-98907d8bdecb" data-action="Deal Block" data-label="The Osmo Pocket 3 has been on sale throughout Prime Day so far. While the base bundle has sold out at Amazon, the Drive bundle (which is basically the basic combo plus a car windscreen mount) is still a very good price. DJI gear is expensive in the U.S. these days due to tariffs (and you can't even get a lot of its newer gear), so you have to take whatever deals you can find." data-dimension48="The Osmo Pocket 3 has been on sale throughout Prime Day so far. While the base bundle has sold out at Amazon, the Drive bundle (which is basically the basic combo plus a car windscreen mount) is still a very good price. DJI gear is expensive in the U.S. these days due to tariffs (and you can't even get a lot of its newer gear), so you have to take whatever deals you can find." data-dimension25="$375" href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0GWV45C6C" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1440px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="vmxWbj6M4Ne3zJqUM3GmJF" name="Osmo Pocket 3" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/vmxWbj6M4Ne3zJqUM3GmJF.jpg" mos="" align="middle" fullscreen="" width="1440" height="1440" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Osmo Pocket 3 has been on sale throughout Prime Day so far. While the base bundle has sold out at Amazon, the Drive bundle (which is basically the basic combo plus a car windscreen mount) is still a very good price. DJI gear is expensive in the U.S. these days due to tariffs (and you can't even get a lot of its newer gear), so you have to take whatever deals you can find.<a class="view-deal button" href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0GWV45C6C" target="_blank" rel="nofollow" data-dimension112="27e6f0ee-d2a7-49ab-968f-98907d8bdecb" data-action="Deal Block" data-label="The Osmo Pocket 3 has been on sale throughout Prime Day so far. While the base bundle has sold out at Amazon, the Drive bundle (which is basically the basic combo plus a car windscreen mount) is still a very good price. DJI gear is expensive in the U.S. these days due to tariffs (and you can't even get a lot of its newer gear), so you have to take whatever deals you can find." data-dimension48="The Osmo Pocket 3 has been on sale throughout Prime Day so far. While the base bundle has sold out at Amazon, the Drive bundle (which is basically the basic combo plus a car windscreen mount) is still a very good price. DJI gear is expensive in the U.S. these days due to tariffs (and you can't even get a lot of its newer gear), so you have to take whatever deals you can find." data-dimension25="$375">View Deal</a></p></div><div class="product"><a data-dimension112="670b320f-02c7-4eb4-807b-185f43213989" data-action="Deal Block" data-label="There's nothing else to say about the Instax mini 12 other than it's a whole lot of fun. It's our favorite instant camera for beginners and, with this deal, is cheaper than ever." data-dimension48="There's nothing else to say about the Instax mini 12 other than it's a whole lot of fun. It's our favorite instant camera for beginners and, with this deal, is cheaper than ever." data-dimension25="$79.95" href="https://www.amazon.com/FUJIFILM-INSTAX-MINI-Pink-Bundle/dp/B0FY3LN7J7/" 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:45.20%;"><img id="pLt9cZa4theve2rMc6pGcX" name="Instax-mini-12-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/pLt9cZa4theve2rMc6pGcX.jpg" mos="" align="middle" fullscreen="" width="1500" height="678" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>There's nothing else to say about the Instax mini 12 other than it's a whole lot of fun. It's our favorite instant camera for beginners and, with this deal, is cheaper than ever. <a class="view-deal button" href="https://www.amazon.com/FUJIFILM-INSTAX-MINI-Pink-Bundle/dp/B0FY3LN7J7/" target="_blank" rel="nofollow" data-dimension112="670b320f-02c7-4eb4-807b-185f43213989" data-action="Deal Block" data-label="There's nothing else to say about the Instax mini 12 other than it's a whole lot of fun. It's our favorite instant camera for beginners and, with this deal, is cheaper than ever." data-dimension48="There's nothing else to say about the Instax mini 12 other than it's a whole lot of fun. It's our favorite instant camera for beginners and, with this deal, is cheaper than ever." data-dimension25="$79.95">View Deal</a></p></div><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-deals"><span>We're tracking all the best Prime Day deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=cameras&min_discount_ratio=0.95&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button, .tg-df-container .tg-df-card-cta { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover, .tg-df-container .tg-df-card-cta:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getRowsLimit() {          if (this.rowsSelect && this.rowsSelect.value) return parseInt(this.rowsSelect.value, 10);          let configSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {             configSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {             configSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(configSource);          if (params.has('rows')) return parseInt(params.get('rows'), 10) || 12;          return 12;        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = this.getRowsLimit();          } else {             this.displayLimit += this.getRowsLimit();          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    url.searchParams.append('rows', this.getRowsLimit().toString());          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = this.getRowsLimit();          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += this.getRowsLimit();                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');               document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve spent 10 days testing the new Leica SL3-P — 40fps and Leica’s most advanced autofocus yet help it finally take the fight to Canon and Sony ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-sl3-p-review</link>
                                                                            <description>
                            <![CDATA[ The Leica SL3-P is a powerful hybrid camera packing a 44MP full frame sensor for beautiful photos, 8K Open Gate, strong dynamic range performance, and more. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">G2D6DCGPxidDY89H4gUveR</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/MacjWovY6eSwvbHoVEVRSS-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 25 Jun 2026 13:05:00 +0000</pubDate>                                                                                                                                <updated>Fri, 03 Jul 2026 13:53:07 +0000</updated>
                                                                                                                                            <category><![CDATA[Mirrorless Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/MacjWovY6eSwvbHoVEVRSS-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A Leica SL3-P full frame mirrorless camera]]></media:description>                                                            <media:text><![CDATA[A Leica SL3-P full frame mirrorless camera]]></media:text>
                                <media:title type="plain"><![CDATA[A Leica SL3-P full frame mirrorless camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/MacjWovY6eSwvbHoVEVRSS-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Leica continues to refine its SL-System of full frame mirrorless cameras and has now delivered its most comprehensive shooter yet. The Leica SL3-P joins the SL3 and the SL3-S, and it introduces handy upgrades that make it a capable hybrid camera. Its 44MP sensor captures beautiful images with plenty of detail, so you can easily create extreme crops. A new hybrid autofocus system and 40fps drive (fastest on a Leica camera yet) make the SL3-P great for wildlife and sports photography, too.</p><p>And that’s not all. For the first time, a Leica camera features 8K/30fps Open Gate video, and alongside that, you get up to 5-stops of IBIS, as well as strong dynamic range performance. If you can afford it, the SL3-P is one of the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a> you can buy.</p><p>To find out how I got on with the camera, read my full Leica SL3-P review.</p><h2 class="article-body__section" id="section-leica-sl3-p-review-specs"><span>Leica SL3-P review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.adorama.com" target="_blank" rel="nofollow">$6,690</a> / <a href="https://www.lcegroup.co.uk/" target="_blank" rel="nofollow">£5,150</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>44MP BSI CMOS full frame</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>Leica Maestro IV</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>5-axis IBIS, up to 5 stops</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Intelligent Hybrid AF</p></td></tr><tr><td class="firstcol " ><p><strong>Focus modes</strong></p></td><td  ><p>Intelligent AF, Continuous AF, Single AF, Touch AF, Manual focus</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>OLED EVF, approx. 5.76 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3.2-inch backlit LED touchscreen, approx. 2.33 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>ISO range</strong></p></td><td  ><p>50-200,000</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>8.1K/25fps Open Gate | 8K/30fps full frame | 7.2K/30fps Open Gate | 6K/60fps</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>USB-C, HDMI Type-A, 3.5mm audio out/in, 1x CFExpress Type-B, 1x SD/SDHC/SDXC UHS-II</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless/bluetooth</strong></p></td><td  ><p>Wi-Fi, Bluetooth</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>40fps (electronic) | 7fps (mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Max shutter speed</strong></p></td><td  ><p>30s to 1/16,000s (stills, electronic) | 30m to 1/8,000s (stills, mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>383 shots | 1,431 shots (adapted shooting cycle)</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>5.96 x 4.25 x 3.33 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>1.69lbs (body only)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-leica-sl3-p-review-price-availability"><span>Leica SL3-P 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="v3q64KA4NMpKhcNLFFVSmQ" name="Leica_SL3P_-3" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/v3q64KA4NMpKhcNLFFVSmQ.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>Leica’s SL-System of full frame mirrorless cameras welcomed a new family member in June 2026. The Leica SL3-P joins the hybrid <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-sl3-review">SL3</a> ($7,485) and the video-first <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/ive-been-testing-the-leica-sl3-its-the-only-camera-thats-made-me-want-to-ditch-my-fujifilm">SL3-S</a> ($5,665). Though it carries a premium price tag, the SL3-P sits between both cameras with a retail price of <a href="https://www.adorama.com" target="_blank" rel="nofollow">$6,690</a> / <a href="https://www.lcegroup.co.uk/" target="_blank" rel="nofollow">£5,150</a>.</p><p>As is the nature of Leica cameras and glass, you’ll need to splurge to get a functioning kit. For example, I tested the Vario-Elmarit-SL 28-70 f/2.8 ASPH lens which costs<a href="https://www.adorama.com/leica-vario-elmarit-sl-28-70-mm-f-2-8-aspherical-lens-l-mount/p/lc11196" target="_blank" rel="nofollow"> $2,190 at Adorama</a> / <a href="https://www.cliftoncameras.co.uk/leica-vario-elmarit-sl-1:2828-70-asph" target="_blank" rel="nofollow">£1,750 at Clifton Cameras</a>. There are more expensive lenses available, but the 28-70 is a good starting point, as it’s priced similarly to many of the professional standard zoom lenses from Canon and Sony.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NcPZUhyHoNyNjDfiuGawbS" name="Leica_SL3P_" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/NcPZUhyHoNyNjDfiuGawbS.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>The SL3-P is a hybrid camera that can capture 44MP stills as well as 8K Open Gate video, so on specs alone, it competes with the likes of the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r5-mark-ii-review">Canon EOS R5 Mark II</a> ($4,299) and the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">Canon EOS R6 Mark III</a> ($2,799). You’ve also got other full frame wonders like the 50MP <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II</a> ($6,499) which can also shoot 8K/30fps video. Price is a different matter, as the SL3-P is priced like a medium-format shooter, like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/hasselblad-x2d-ii-100c-review">Hasselblad X2D II 100C</a> ($7,400) or the<a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/fujifilm-gfx100rf-review"> Fujifilm GFX100RF</a> ($4,899).</p><h2 class="article-body__section" id="section-leica-sl3-p-review-design"><span>Leica SL3-P 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="6SKmexDUp7wVCZMeSiS8yQ" name="Leica_SL3P_-2" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/6SKmexDUp7wVCZMeSiS8yQ.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>All three Leica SL3 bodies share similar designed philosophies. The SL3-P, just like its siblings, is covered in a faux leather-like material that makes it look premium and like you’ve spent a lot of money on it (which you will have). Leica forgoes the classic red dot on the SL3-P, though, and the only branding you get is the brand’s name in big bold lettering above the sensor.</p><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="i28vrZY6pWQBExtwDiK7DS" name="Leica_SL3P_-26" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/i28vrZY6pWQBExtwDiK7DS.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>What the SL3-P does retain, though, is the ginormous sculpted grip which is spacious enough for you to wrap your fingers around. This makes the camera a joy to handle, whether you’re using it one-handed or with both.</p><p>The SL3-P’s body is slightly bigger than the base SL3 as it now measures 5.96 x 4.25 x 3.33 inches, but still weighs 1.69lbs (without cards and battery). The overall handling will naturally depend on the lens you use. The Vario-Elmarit-SL 28-70 lens I tested weighs 1.26lbs, so I didn’t find the kit too heavy or unwieldy to use.</p><h2 id="viewfinder">Viewfinder</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="snmdLynECD2csekTMP62aR" name="Leica_SL3P_-14" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/snmdLynECD2csekTMP62aR.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>Another similarity between the SL3 and the SL3-P is the OLED EVF with a resolution of approximately 5.76 million dots. In my testing, I found it easy and reliable to look through and frame my subjects. You get a refresh rate of either 60Hz or 120Hz which can be changed via the camera’s menu system. 120Hz is more than sufficient for most kinds of photography, including tracking fast-moving subjects like birds or sportspersons — it will naturally have a detrimental effect on the battery life, though.</p><p>But I have the same complaint I had with the SL3: I wish the SL3-P’s viewfinder had a higher resolution, especially for the price. The SL3-P’s EVF resolution is the same as that of the Canon EOS R5 Mark II, the Hasselblad X2D II 100C, and the Fujifilm GFX100RF. However, the Sony A1 II and A7R VI have 9.44-million dot EVFs, which are phenomenal, and help make the most of their high-res sensors when focus checking. </p><p>The SL3-P is, essentially, another version of the SL3, so I can understand why Leica has kept the same viewfinder — perhaps we’ll see a better one on the next standalone camera, as and when it comes. SL4th time’s the charm?</p><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.20%;"><img id="MacjWovY6eSwvbHoVEVRSS" name="Leica_SL3P_-24" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/MacjWovY6eSwvbHoVEVRSS.jpg" mos="" align="middle" fullscreen="" width="2048" height="1151" 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 though Leica has added 40fps burst shooting to the SL3-P — up from 15fps on the SL3 and 30fps on the SL3-S — the EVF isn’t blackout-free. This means that, when viewing and photographing subjects through the EVF, you get short black screens between shots. When you’re shooting at a blistering 40fps, you won’t always notice them, but this will certainly be an issue for professional wildlife and sports photographers. If you’re one of those people, you may want to grab the Sony A1 II or the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/the-canon-eos-r6-v-and-the-sony-a7r-vi-dropped-this-week-both-look-incredible-but-heres-the-one-im-most-excited-for-and-why">Sony A7R VI</a> ($4,499), both of which boast blackout-free sequential shooting.</p><h2 id="monitor-displays">Monitor / Displays</h2><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="orG5KwR6jv3yirKV9QjE8R" name="Leica_SL3P_-15" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/orG5KwR6jv3yirKV9QjE8R.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>The SL3-P sports dual displays. You get a 3.2-inch backlit LED touchscreen monitor on the back, with a resolution of 2.33 million dots — almost as much as the Hasselblad X2D II 100C (2.36M dots) and higher than the Canon EOS R6 Mark III (1.6M dots). In most lighting conditions, I’ve found it plenty bright and easy to read, even in direct sunlight. Similar to the SL3, the SL3-P’s monitor features an anti-fingerprint and anti-scratch coating — reassuring given how much this camera costs.</p><p>Similar to the Hasselblad X2D II 100C’s monitor, the SL3-P’s rear display can be tilted up and down by 90°, enabling you to shoot at awkward angles, such as with the camera at your hip — great for street photography when you want to capture candid moments without drawing attention to yourself.</p><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="cu3Z8E2T7kZ3342wv7mbaR" name="Leica_SL3P_-9" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/cu3Z8E2T7kZ3342wv7mbaR.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>Then you’ve got a 1.28-inch top display, located next to the dials on the top plate’s right-hand corner. This screen shows your active settings: shutter speed, ISO, aperture, AF mode, and so on and so forth. Its trans-reflective monochrome design makes the display easy to read, especially outdoors. You’ll find a similar display on the Hasselblad X2D II 100C, too.</p><h2 id="ports">Ports</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="WNsYpjGF7N8TnaPWq4rfeR" name="Leica_SL3P_-20" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/WNsYpjGF7N8TnaPWq4rfeR.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>There are barely any differences, if any at all, between the ports on the SL3-P, SL3 and SL3-S. Located on the camera’s left edge, you’ll find HDMI 2.1 Type-A, USB-C for charging and data transfer, and 3.5mm audio output and input ports. All of these do their job as intended.</p><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="u9pxxkwsv6JtRQ9gFnEzcS" name="Leica_SL3P_-19" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/u9pxxkwsv6JtRQ9gFnEzcS.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>On the left edge, you’ll find slots for SD/SDHC/SDXC UHS-II/UHS-I as well as CFExpress Type-B cards. I shot on the <a href="https://www.amazon.com/Lexar-Professional-CFexpress-Recording-LCXEXPR128G-RNENG/dp/B0C1TPNNTK/" target="_blank" rel="nofollow">Lexar 128GB Gold Series ($198)</a> which boasts up to 1750MB/s read and 1500MB/s write speeds. CFExpress support is welcome, as these cards ensure faster read and write speeds versus SD cards, which is needed for the 44MP files as well as sustaining the 40fps drive. You can essentially shoot for longer before filling buffers.</p><h2 id="weather-sealing">Weather-sealing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aZoc3osf4843m8sXG5mmQS" name="Leica_SL3P_-25" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/aZoc3osf4843m8sXG5mmQS.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>Just like the SL3 and the SL3-S, the SL3-P is IP54-rated, which means it’s protected against dust, moisture and light rain. That’s thanks to an entirely sealed full-metal housing. This is similar weather-sealing you’ll find on the Canon EOS R5 Mark II and the Sony A1 II. Meanwhile, the Hasselblad X2D II 100C isn’t fully weather-sealed, so the SL3-P has the upper hand here.</p><p>Obviously, you'll need to ensure you're using a weather-sealed lens for any of that to matter. The Vario-Elmarit-SL 28-70 f/2.8 ASPH I tested is, in that regard, weatherproof.</p><h2 class="article-body__section" id="section-leica-sl3-p-review-controls"><span>Leica SL3-P review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZF7CxDevNVrZLvkQmi5Y8S" name="Leica_SL3P_-16" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/ZF7CxDevNVrZLvkQmi5Y8S.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>The Leica SL3-P is basically the SL3 but more refined and well-rounded, and it sports practically the same control scheme you’ll find on the base model. By that I mean it’s stripped-back, keeping in line with Leica’s minimalist ethos.</p><p>I’ve loved using the SL3-P as much as I did the SL3. The buttons and dials feel well-placed and within easy reach, and they add to the sublime user experience. I’m happy to see the rubber-covered joystick make an appearance on the SL3-P too, as it feels comfortable to use for long periods of time. I’ve found it more enjoyable to use than the textured joysticks on the Canon EOS R5 Mark II and the Sony A1 II.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kD6Zovr62T9Fwb8EzWMiTR" name="Leica_SL3P_-8" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/kD6Zovr62T9Fwb8EzWMiTR.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>Leica’s menu system deserves a shoutout too, as it’s uncomplicated and effortless to navigate. The eight settings tiles can be customized to complement six customizable physical buttons, which makes the camera highly personalizable. And it doesn’t stop there. The SL3-P features six customizable profiles for photo and another six for video, making the camera ideal for hybrid shooters.</p><h2 class="article-body__section" id="section-leica-sl3-p-review-autofocus"><span>Leica SL3-P review: Autofocus</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Y4wMuTW9wfP54VGLzGoFPS" name="Leica_SL3P_-22" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/Y4wMuTW9wfP54VGLzGoFPS.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>One of the biggest upgrades to the Leica SL3-P comes in the form of a new autofocus system. In addition to utilizing a combination of phase-detection for speed and contrast-detection for accuracy, like the SL3, the SL3-P also uses depth mapping. It borrows depth mapping from the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-q3-43-review">Leica Q3 43</a> ($7,380), and it basically builds a 3D digital map of subjects to instantly lock onto them, regardless of unpredictable movements.</p><p>The result is an intelligent AF system that quickly detects and locks onto birds, animals, and humans. You can choose from Single AF (AFs), Continuous AF (AFc) and Manual Focus, or you can select Intelligent AF where the camera will decide whether to use AFc or AFs — handy for when you’re photographing sports or wildlife, or when you know your subject’s movements can be unpredictable.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/vyyaPgU92p5op3tppPvv7V.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/xYB33ii87aYBi642R9FnnS.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/riEqBKAdhLEfyJpmKhvBkT.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The SL3-P’s autofocus worked extremely well in my testing, regardless of the subject I was photographing or the lighting conditions. When subject detection is set to Human Eye/Body, the camera cycles through the subject’s different body parts. If it can’t find eyes, for instance, it’ll look for the face, then the whole body, in a similar way to AF systems from other manufacturers. It worked effectively when I photographed my friends at a birthday party. In the photos in the gallery above, the SL3-P was quick to lock onto their eyes and faces, and the resulting images are crisp and sharp.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/fMrnMfcFLi6VZxMEqG8jhX.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/oP4baSJuL6XDisYWtnNbAW.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/CqoRPbBhanE8pFy7uZ53YY.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Animal/bird detection works just as well, too, making the SL3-P a formidable wildlife camera. Whether I was capturing birds in flight, while they were diving, or when they were stationary, the camera never let me down. In the first photo in the gallery above, even though the moorhens were slightly obscured by the grass, the SL3-P quickly locked onto them.</p><p>The second photo of the duck was taken in burst mode (more on that shortly) and I’d been tracking it as it dove underwater and then broke the surface, and the SL3-P locked onto it as soon as it came back out. I was also impressed by the SL3-P locking onto the heron’s wing in the third slide, and I was able to capture an in-focus shot of the park’s resident elusive bird.</p><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:66.65%;"><img id="PCkNm5FmSYeSbmBxRpBKAY" name="Leica-SL3P-sample-24" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/PCkNm5FmSYeSbmBxRpBKAY.jpg" mos="" align="middle" fullscreen="1" width="2048" height="1365" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/PCkNm5FmSYeSbmBxRpBKAY.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: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>I found the SL3-P’s focus joystick extremely responsive in my testing too. Like I mentioned earlier, it’s comfortable, and it promptly registers your commands. I’ve used it plenty of times to reset focus points, or focus on particular subjects — like the second pigeon flapping its wings in the photo above.</p><h2 class="article-body__section" id="section-leica-sl3-p-review-stabilization"><span>Leica SL3-P review: Stabilization</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/G58yniBuXmVgrFRnE6P5zS.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption>Taken at 1s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ouaZJzT2MKinWdY9KK37rS.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption>Taken at 1.5s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The Leica SL3-P retains the SL3’s 5-axis in-body image stabilization (IBIS) and offers up to 5-stops, enabling you to capture images at slow shutter speeds and in low-light environments. The Vario-Elmarit-SL 28-70 lens I tested doesn’t feature stabilization so I had to rely on the camera’s IBIS when taking photos at slow shutter speeds.</p><p>In the gallery above, the first image was captured at 1s and it’s sharp and stable, with no blurry edges. Any slower than that, such as at 1.5s in the second slide, and the images came out blurry, impacted by camera shake. I’ve been satisfied with this performance. For a frame of reference, on a camera with up to 8.5-stops of IBIS, like the Canon EOS R6 Mark III, I’ve been able to shoot at speeds as slow as 4s.</p><h2 class="article-body__section" id="section-leica-sl3-p-review-image-quality"><span>Leica SL3-P review: Image quality</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hPG5jTjHzN3RDtyZGQxqrQ" name="Leica_SL3P_-7" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/hPG5jTjHzN3RDtyZGQxqrQ.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>I can’t remember the last time I complained about a Leica’s imaging prowess — that’s because I’ve never done so. It’s really difficult to fault it, and the Leica SL3-P is no different. The camera packs a 44MP BSI CMOS full frame sensor. That’s down from 60.3MP on the SL3 (and up from 24.6MP on the SL3-S) but remember, the SL3-P introduces features like 8K Open Gate as a trade-off, which I’ll discuss in detail shortly.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/fnSAYyt6PaaidREWEuMX6U.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hdtKZgQotL6okoXG9G5pPW.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6ocTXPAr5ZC6WyzPQRxYgT.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zvLFZ3RHpmzmT3xe693VyT.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Jg6wKgyzR2eTmoxWCsgYeX.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jgnp5ayDP8BfZFACuGo3dW.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/3dt9mHcpsyJ92qbmcjWmZW.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/aFFRvACeHveBxUoRyRBgbV.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/yCBYqXNz4Hu2rir7janGtU.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/w7zBfvuB3VnKAkYFxnNVYU.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/XKZxjtfddNyWSEbWfUDE4Z.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Working in conjunction with the Leica Maestro IV processor, the photos the SL3-P captures are nothing short of spectacular. Colors are vibrant and bright without appearing oversaturated or too heavy in contrast, and they appear true to life. I love how much detail the SL3-P’s sensor captures, especially when you’re using a sharp lens like the Vario-Elmarit-SL 28-70 f/2.8 ASPH. with it. The water droplets on the swan’s head and the individual feathers in the first image, for instance, appear extremely sharp, adding depth and texture to the image.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/PP97aXYbvSTsFn2ebqCWWU.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/V5g4VX2bRMGxWbMdNjvjaY.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ekGuakqdfrMs5WFDmqaTpT.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6CRMwuSwqucMHvRRhjpe3X.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The SL3-P’s 44MP sensor provides plenty of resolution, especially for making tight crops. In the gallery above, the first image is an extreme crop of the second, and the third of the fourth. As you can see, I’ve been able to crop in a lot without losing much detail. The pigeon’s feathers and face still look sharp, and the clock’s features and the bricks surrounding it appear detailed.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/i48uLwhxzkVGkkeEG84tGU.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9qXVW5bVWxWGb3J9LyaXwW.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZfPnbQuGg7DsYU6pH3HzBZ.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Similar to many other Leica cameras, the SL3-P features a few Leica Looks or color profiles, including Natural, Vivid, Monochrome, and Monochrome High Contrast. Monochrome High Contrast is my favorite, and I like it almost as much as Fujifilm’s Acros black-and-white film simulation. The first two photos in the gallery above were captured in this filter, and I love how cinematic the photos look with deep blacks. It’s the perfect color profile for portraits.</p><p>The third photo was captured in the Monochrome profile, and it appears more flat and balanced, making it great for architecture and street photography. The final photo of the fern leaves was shot in Vivid, and it made the greens look more saturated and high on contrast, and that’s given the image a three-dimensional effect.</p><p>Just like on the SL3-S, you get access to Multishot Mode on the SL3-P too. The camera uses pixel-shift technology to produce an image that’s four times larger than the sensor. Basically, it takes eight to 16 individual frames and stacks them to create a 176MP image. If you’re taking photos with the SL3-P mounted on a tripod, this is a fantastic feature to have, especially for portraits and product photography. Naturally, these files are massive, with a JPEG around 230MB, and due to website limitations, I unfortunately can’t show you how detailed they look. </p><h2 id="high-speed-drive">High-speed drive</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/wveAu6kEEKr8LKZ2MXG9bV.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/qAgsXm4motQWGjNGERsbEX.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/a5mxD7EmMAR5DxzjacUYKU.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>40fps high-speed drive is one of the SL3-P’s headline features, and as I mentioned earlier, it’s up from just 15fps on the SL3 and 30fps on the SL3-S. The 40fps sequential images are captured in 12-bit color depth, which means they comprise over 68 billion shades. If you want 14-bit color depth, you’ll need to drop the frame rate to 25fps.</p><p>Being able to capture 40 frames per second is enough for photographing most wildlife and sports, at least at an amateur or hobbyist level. Obviously, though, pros may need more than that, like the 120fps drive offered by the Sony A1 II or <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-review">OM System OM-1 Mark II</a> ($2,399). I found 40fps more than sufficient in my testing. The electronic shutter with its faster shutter speed of 1/16,000s gave me plenty of wiggle room too.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/4KuTM27M4DywWTa8YJvmDV.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zXueLbKNF8WQ7Q4ZbGsNzX.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zCAj5ij8YjMhUKg89JMeCV.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Thanks to 40fps sequential shooting and the reliable autofocus, I was able to capture many photos of birds in action, like the two photos of the seagulls fighting over a piece of bread. The camera was quick to focus and lock onto them. As the seagull on the right started to fly away, I started tracking it and the SL3-P, again, kept focused on the bird.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/kp6J2cVyiVKfSJCECPMgyV.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VBonzfWaejgcA6VhhP2fvV.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fDRNb32hS7Rqr3ywyzJkPW.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/oXog7btNFVJwZMbuN4HVQW.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>It’s important to note that unlike professional wildlife or sports cameras like the Canon EOS R5 Mark II, Sony A7R VI or the Sony A1 II, the SL3-P doesn’t feature pre-burst capture, where the camera starts buffering images when you half-press the shutter button. If you’re a professional, you may want to consider either of those cameras. For hobbyists, though, the SL3-P should suffice.</p><h2 id="iso-performance">ISO performance</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/9VDzwpRp2h3WZNmuAFLyST.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption>ISO12,500, JPEG export of DNG file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/J6y8QhVKNCuqmJNKLkpnGT.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption>ISO12,500, SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The SL3-P has an ISO range of 50 to 200,000, and the top-end of that range is double that of the SL3’s (100,000). Instead of capturing RAW files, the SL3-P captures DNGs alongside JPEGs, and these files are 15-20% smaller than RAW files. The difference is hardly noticeable though.</p><p>Images are as noise-free as you’d expect at ISO6400, so let’s talk about high ISO values instead. The images in the gallery above were shot at ISO12,500: the first is a JPEG export of the DNG file while the second is the straight-out-of-camera (SOOC) JPEG. As you can see, the DNG file is fairly noisy but the SL3-P has done a good job of reducing the grain, and there’s very little loss in sharpness either.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/VZ2rBeQJ3h7RquGewAaQeT.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption>ISO25,000, JPEG export of DNG file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eYsJtD7kpgCF3kkUKU4MHT.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption>ISO25,000, SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I then increased the ISO to 25,000 and the camera still did a decent job of smoothening out the noise in the SOOC JPEG (second slide). But you start to lose a little sharpness here, as can be seen in the bobblehead’s chin and the sculpted creases in its trousers. It’s still a usable image, though.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/xurLubrwNE8coxesMH6SUU.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption>ISO32,000, JPEG export of DNG file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/y9YudebQAg9nCVv9PuV2TT.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption>ISO32,000, SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I wouldn’t really recommend pushing past ISO32,000 on the SL3-P. The images above were shot at ISO32,000, and as you can see in the SOOC JPEG (second slide), the edges of the bobblehead’s hair don’t look as sharp anymore. The book in the background is quite noisy too. This is similar to the Sony A1 II’s and the Canon EOS R5 Mark II’s high ISO performance.</p><h2 id="dynamic-range">Dynamic range</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/QW2CzDuZHvppkqewWXm79Z.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6je6Qyz3c7eTv9oYPd5z5Z.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>One of the things I was most impressed by when testing the SL3 was the camera’s dynamic range performance, and the SL3-P hasn’t let me down either. Offering up to 14+ stops at base ISO50, the SL3-P enables you to make extensive adjustments in post-production.</p><p>The first photo in the gallery above is the unedited version of the second. It was an extremely bright day and the flowers were backlit by the sun. I brought the highlights all the way down and lifted the shadows in Adobe Photoshop Lightroom, and as you can see in the second slide, the camera’s done a great job of retaining highlight detail. The clouds in the sky don’t appear blown out and reducing the highlights has revealed a spot of blue sky. I like that the flowers still look lovely and pink, even in the edited image.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/nJGndxdCBpzok8dnmqWKPX.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/PbY7EpNFa4hyfytUHaeDBW.jpg" alt="A photo taken on a Leica SL3-P full frame mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The SL3-P keeps shadows noise-free too, as you can see in the gallery above. When I boosted the shadows and reduced the highlights, it revealed many details, such as the shrubs and bushes down the path, and leaves and branches in the tree closest to the camera.</p><h2 class="article-body__section" id="section-leica-sl3-p-review-video"><span>Leica SL3-P review: Video</span></h2><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="gTdjk3SPshDYVbPZxkJBjS" name="Leica_SL3P_-23" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/gTdjk3SPshDYVbPZxkJBjS.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>Striking a balance between the SL3 and the video-first SL3-S, the Leica SL3-P packs impressive video capabilities. You can shoot 8.1K/25fps Open Gate video in 3:2 format, HDMI RAW 8K Open Gate, and 8K/30fps using the full size of the sensor. There’s also 6K/60fps for smooth motion as well as 4K/120fps for slow-mo footage.</p><p>All of these resolutions and frame rates are recorded in 4:2:2 10-bit (in MOV) which means the camera can capture 1.07 billion individual hues. Leica has also added two new in-camera LUTs, Leica Pure and Leica Cinema, for the L-Log color space, giving you lots of freedom in post-production and color grading. An important distinction between the SL3-S and the SL3-P is that the former can shoot ProRes RAW, making it a strong choice for professional video work.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/JtbXYEQKPTQ" allowfullscreen></iframe></div></div><p>The compilation of clips above was shot in 4K at 60fps, and it looks beautiful. The colors look great; all the subjects are in focus thanks to the SL3-P’s excellent autofocus; and the video looks smooth throughout. I also like that even though the wind was blowing, there’s no noticeable buffeting, and the SL3-P’s built-in mic has done well to record the birds chirping or squawking.</p><p>I was also impressed with the SL3-P’s cooling system. I didn’t experience any severe overheating while recording at 4K/60fps outdoors. 8K Open Gate takes a toll on recording times, though, and Leica says the SL3-P can record for approximately 20 minutes at 77°F / 25°C. You may want to consider the Leica SL3-S instead if you’re a videographer, as it can typically expect 45 to 60 minutes of continuous 6K recording.</p><h2 class="article-body__section" id="section-leica-sl3-p-review-battery-life"><span>Leica SL3-P 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="3Nxi4jCdTgCrgSLJeRye2R" name="Leica_SL3P_-21" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/3Nxi4jCdTgCrgSLJeRye2R.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>Leica has made improvements to its SL-System’s battery life as the SL3-P is now CIPA-rated for 383 shots — up from 260 shots on the SL3. When using Leica’s adapted shooting cycle, the German manufacturer claims the camera’s BP-SCL6 battery pack can yield 1,431 stills.</p><p>In my testing, I was able to capture over 2,000 photos (including burst shots) and a few minutes of 4K/60fps video before needing to charge the camera. Your mileage will, of course, depend on your shooting settings, so it’s best to take the CIPA rating as the baseline.</p><p>The SL3-P’s CIPA-rated battery life puts it on a level playing field with the Canon EOS R5 Mark II (340 shots). The Sony A1 II and the Sony A7 V can still last longer, with 420 and 630 CIPA-rated batteries respectively. Meanwhile, the Hasselblad X2D II 100C is CIPA-rated for 466 frames, and we managed 246 shots (RAW + JPEG) from around 55% of battery in our testing.</p><h2 class="article-body__section" id="section-leica-sl3-p-review-verdict"><span>Leica SL3-P 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="9uvHqRPdWimsdTQzojLxxQ" name="Leica_SL3P_-4" alt="A Leica SL3-P full frame mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/9uvHqRPdWimsdTQzojLxxQ.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>The Leica SL3-P is the most complete SL-System camera Leica has produced to date, and it successfully blends high-resolution photography with advanced video capabilities. Its 44MP full frame sensor captures stunning images while retaining plenty of detail, and there’s enough resolution for aggressive cropping, too. The new hybrid autofocus system and 40fps burst shooting make it more than capable for wildlife, sports and action photography.</p><p>In addition to all this, the SL3-P delivers strong dynamic range performance, dependable in-body image stabilization, and videographers will appreciate the 8K Open Gate capabilities. The premium build quality, intuitive controls, and the refined shooting experience reinforce the SL3-P’s appeal.</p><p>As is the nature of Leica cameras, the SL3-P doesn’t come cheap, but it’s priced similarly to pro snappers from Canon and Sony. Professional photographers may be put off by the lack of a blackout-free EVF, too. But if these are compromises you’re willing to make, the SL3-P’s strengths are far too many to be overlooked. It’s an exceptional camera and it finally puts Leica on a level playing field with Canon and Sony.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ This thermal camera for my phone gives me instant night vision — and it's $200 off right now ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/phones/this-thermal-camera-for-my-phone-gives-me-instant-night-vision-and-its-usd200-off-right-now</link>
                                                                            <description>
                            <![CDATA[ I've been testing the Thermal Master P4 and this compact thermal camera blends two lenses to deliver sharp images with heat signature. And it's $200 off for Prime Day. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">PJt7XWSfazchxyVuhKgUbH</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/4JBeib78hsgRSW3G2qBfrm-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 25 Jun 2026 10:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Phones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></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/4JBeib78hsgRSW3G2qBfrm-1280-80.jpg">
                                                            <media:credit><![CDATA[John Velasco / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Thermal Master P4 connected to an Android phone.]]></media:description>                                                            <media:text><![CDATA[Thermal Master P4 connected to an Android phone.]]></media:text>
                                <media:title type="plain"><![CDATA[Thermal Master P4 connected to an Android phone.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/4JBeib78hsgRSW3G2qBfrm-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>There’s something inherently sci-fi about fastening an accessory to my phone and suddenly getting Predator-vision. In the past couple of years, I’ve tested a bunch of smartphone-connected thermal dongles on the market, including the gear from Thermal Master.</p><p>It started with the <a href="https://www.tomsguide.com/phones/forget-flir-i-tried-this-cheap-thermal-camera-for-my-phone-to-see-in-the-dark">budget-priced P1</a>, followed by the <a href="https://www.tomsguide.com/phones/my-new-favorite-phone-accessory-attaches-to-my-keychain-and-goes-everywhere-i-do-plus-it-lets-me-see-in-the-dark">micro-sized P2</a> that fits on my keychain, and finally the <a href="https://www.tomsguide.com/phones/i-just-gave-my-iphone-night-vision-with-this-usb-c-thermal-camera-and-now-i-can-see-in-the-dark">P3 that introduced thermal vision to my iPhone</a>. With its latest release, the <a href="https://thermalmaster.com/products/thermal-master-p4-thermal-in-one-all-in-one" target="_blank" rel="nofollow">Thermal Master P4</a> steps up as an all-in-one model with dual lenses to deliver outstanding clarity and heat signature performance.</p><p>I’ve been using it around my home for the past few weeks now. From checking out what’s lurking outside while I’m shooting astrophotography, to finding gaps around windows and doors that are letting hot air inside my home, it shows me stuff I simply can’t see with my own eyes. And best of all, it’s <a href="https://www.amazon.com/Thermal-Master-P4-Android-Resolution/dp/B0GL7G2JPL/" target="_blank" rel="nofollow">$200 off right now for Prime Day</a>.</p><div class="product"><a data-dimension112="eb5499d2-dbbf-462b-a24e-f67268941c14" data-action="Deal Block" data-label="Price check: $299 @ Amazon" data-dimension48="Price check: $299 @ Amazon" data-dimension25="$299" href="https://thermalmaster.com/products/thermal-master-p4-thermal-in-one-all-in-one" 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="bdyqRpE3BZSY979hCMLrMh" name="Thermal Master P4 deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bdyqRpE3BZSY979hCMLrMh.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This compact thermal camera fits in your pocket and attaches to an Android phone to provide instant night vision for tracking heat signatures. Its dual-lens system offers impressive performance, making it easy to spot varying temperature differences while seamlessly compositing the two images.<br><strong>Price check: </strong><a href="https://www.amazon.com/Thermal-Master-P4-Android-Resolution/dp/B0GL7G2JPL/" target="_blank" rel="nofollow" data-dimension112="eb5499d2-dbbf-462b-a24e-f67268941c14" data-action="Deal Block" data-label="Price check: $299 @ Amazon" data-dimension48="Price check: $299 @ Amazon" data-dimension25="$299"><strong>$299 @ Amazon</strong></a><a class="view-deal button" href="https://thermalmaster.com/products/thermal-master-p4-thermal-in-one-all-in-one" target="_blank" rel="nofollow" data-dimension112="eb5499d2-dbbf-462b-a24e-f67268941c14" data-action="Deal Block" data-label="Price check: $299 @ Amazon" data-dimension48="Price check: $299 @ Amazon" data-dimension25="$299">View Deal</a></p></div><h2 id="blends-thermal-vision-and-reality-to-undercover-gaps">Blends thermal vision and reality to undercover gaps</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/24xtTUVyR4K32BRFi3y5ih.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/NUC9Xo2BG6DUBg7nLDPv8h.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/AVrvCgWGiGof5dWm3nUQ5h.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EjZXHT3ByAudaEq5LJDKzg.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/PfEKNPTi5UocjjQLyDG9jh.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/cBtvwWi2VvS2YLSW39cSNh.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure></figure><p>What really elevates the Thermal Master P4 over previous models I’ve tested is its brilliant dual-lens system. Traditional infrared attachments just show you a blob of heat without distinct edges to add detail to what you’re looking at.</p><p>The Thermal Master P4 solves this by layering a visible light camera over its thermal sensor. This unique, AI-assisted combination of thermal vision and reality allows me to uncover hidden gaps with surgical precision. When I scanned the baseboards and window frames of my home, I wasn't just looking at a generic blue blob of cold air—I could see the exact physical gap in the molding where the draft was coming through. It bridges the gap between digital data and the physical world perfectly.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/EjZXHT3ByAudaEq5LJDKzg.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nHibMh74APLZARNpRNaiJh.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/u95sZ9G6uTHTaaSkXet2sg.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Ywo83WusV8FZjeRKnEsoqg.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nhEsAhgNigHwDPj4CGaLog.jpg" alt="Thermal Master P4 sample." /><figcaption><small role="credit">Future</small></figcaption></figure></figure><p>Keep in mind, this feature is most effective when there’s ample light in the scene. While the P4 has no problem showing heat signatures in complete darkness, the real-world physical details fade away without ambient light, making it harder for the AI to blend the two images. You can see the difference with the shots in the gallery above compared to the ones from the previous gallery.</p><h2 id="doesn-t-lag-like-other-infrared-cameras">Doesn’t lag like other infrared cameras</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="zCzKivqHKUchv8woKqRdtm" name="Thermal Master P4" alt="Thermal Master P4 held in the hand." src="https://cdn.mos.cms.futurecdn.net/zCzKivqHKUchv8woKqRdtm.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: John Velasco / Tom's Guide)</span></figcaption></figure><p>Unlike other thermal sensors that painfully stutter and freeze every time you pan across a room, the P4 handles movement like a champ. It doesn’t lag like other infrared cameras. Instead, it delivers a smooth, high-frame-rate feed that tracks shifting temperatures and moving heat signatures in crisp, real-time fluidity. This is probably the biggest reason why I continue to favor Thermal Master's portable cameras over the ones that <a href="https://www.tomsguide.com/us/flir-one-pro-for-ios,review-4511.html">FLIR makes</a>.</p><h2 id="portable-size-lets-me-carry-it-everywhere">Portable size lets me carry it everywhere</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="QH9QcMFSFVFV6MfTDGShzm" name="Thermal Master P4" alt="Thermal Master P4 in its carrying case." src="https://cdn.mos.cms.futurecdn.net/QH9QcMFSFVFV6MfTDGShzm.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: John Velasco / Tom's Guide)</span></figcaption></figure><p>Most professional thermal imagers are bulky, standalone tools that require their own dedicated carrying cases. The P4, by contrast, features a compact design that slides easily into my pocket — and its plug-and-play functionality makes it a breeze to operate.</p><p>Since it’s something I can carry everywhere, I’ve found myself using it in casual situations where I’d never bother hauling a traditional camera — like when I go for walks down by the beach, or bike riding on a path where I know foxes typically roam.</p><p>With the $200 instant discount going on right now for Prime Day, it’s an absolute steal for anyone wanting a high-performing thermal camera without the bulk or the heavy price tag. Its $499 normal cost is right about what you’d pay for a comparable handheld thermal camera from FLIR, but it’s way more valuable at its current $299 price point.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-XpmMGe"></div>                            </div>                            <script src="https://kwizly.com/embed/XpmMGe.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/phones/motorola-phones/motorola-razr-ultra-2026-review">Motorola Razr Ultra 2026 review: Better does come at a premium price</a></li><li><a href="https://www.tomsguide.com/phones/simo-solis-lite-hotspot-review">I tested this puck shaped mobile hotspot — and it’s the simplest way to get online</a></li><li><a href="https://www.tomsguide.com/phones/best-early-prime-day-2026-phone-accessories-deals-magsafe-wallets-power-banks-chargers-and-more">I tested 100+ phone accessories in the last year and these are my favorite early Prime Day deals — MagSafe wallets, power banks, chargers, and more</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ A lot of Prime Day action camera deals suck: These ones don't ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/gopro-action-cameras/prime-day-action-camera-deals-are-live-upgrade-your-content-for-less</link>
                                                                            <description>
                            <![CDATA[ There are some surprisingly good deals on action and 360 cameras this Prime Day! Here are my favorites. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">EaPRU9Lm6doCmKqikg65wj</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/4QRFb2Nb9SEpYeUzosLCoe-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 23 Jun 2026 13:18:54 +0000</pubDate>                                                                                                                                <updated>Wed, 24 Jun 2026 08:50:49 +0000</updated>
                                                                                                                                            <category><![CDATA[GoPros &amp; Action Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></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/4QRFb2Nb9SEpYeUzosLCoe-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A person holding the DJI Osmo Pocket 3 in hand]]></media:description>                                                            <media:text><![CDATA[A person holding the DJI Osmo Pocket 3 in hand]]></media:text>
                                <media:title type="plain"><![CDATA[A person holding the DJI Osmo Pocket 3 in hand]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/4QRFb2Nb9SEpYeUzosLCoe-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The <a href="https://www.tomsguide.com/live/news/prime-day-2026-top-sales-now">Prime Day deals</a> are here, and yesterday I was actually pretty shocked at some of the deals — for better or worse. I'll start with the better! There are some surprisingly good deals on the <a href="https://www.tomsguide.com/us/best-action-cameras,review-3046.html">best action cameras</a> (rivalling Black Friday prices), including $200 off the GoPro MAX2 at Amazon — one of my favorite <a href="https://www.tomsguide.com/us/best-360-cameras,review-3737.html">360 cameras</a>. Meanwhile, <a href="https://www.amazon.com/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD" target="_blank" rel="nofollow">the DJI Osmo Pocket 3 — a YouTuber's staple — is currently over $120 off at Amazon</a>. Both are quick and easy ways to upgrade your content for social or YouTube.</p><p>There are some deals to be wary of, though. The <a href="https://www.amazon.com/GoPro-HERO13-Black-Compatability-HB/dp/B0DCM34GXX/" target="_blank" rel="nofollow">GoPro Hero13 Black is <em><strong>seemingly</strong></em> reduced from $429 to $349 at Amazon</a>, which is <em><strong>seemingly</strong></em> a good discount. No! Bad deal!!!! $349 is where it sits most of the year anyway, if not lower. The price just got jacked up to $429 for a couple of weeks to catch you out!</p><p>I'm Pete, senior reviews editor here at Tom's Guide, and I've tested every major action and 360 camera launched for the last few years. I track prices all year, so I've rounded up here which deals are actually good, to help you avoid the scams.</p><p>Without further ado, here are all my favorite Prime Day action camera deals.</p><h2 class="article-body__section" id="section-quick-list"><span>Quick list</span></h2><ul><li><strong>GoPro LIT Hero: </strong><a href="https://www.amazon.com/GoPro-LIT-Hero-Built-Stabilization/dp/B0FP5R9BDZ" target="_blank" rel="nofollow"><strong>was $269 now $179 @ Amazon</strong></a><strong></strong></li><li><strong>DJI Osmo Action 4: </strong><a href="https://www.amazon.com/DJI-Waterproof-Low-Light-Performance-Long-Lasting/dp/B0C783YNW7" target="_blank" rel="nofollow"><strong>was $299 now $179 @ Amazon</strong></a><strong></strong></li><li><strong>Insta360 X3: </strong><a href="https://www.amazon.com/Insta360-Single-Lens-Stabilization-Touchscreen-Wintersports/dp/B0B9H572LC" target="_blank" rel="nofollow"><strong>was $299 now $229 @ Amazon</strong></a><strong></strong></li><li><strong>DJI Osmo Action 5 Pro: </strong><a href="https://www.amazon.com/gp/aw/d/B0DS2DF5S5" target="_blank" rel="nofollow"><strong>was $339 now $288 @ Amazon</strong></a><strong> </strong></li><li><strong>GoPro MAX2: </strong><a href="https://www.amazon.com/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FNGK6JZP" target="_blank" rel="nofollow"><strong>was $499 now $299 @ Amazon</strong></a><strong></strong></li><li><strong>DJI Osmo 360: </strong><a href="https://www.amazon.com/DJI-Standard-Imaging-Recording-Waterproof/dp/B0DTTZZ6Q3/" target="_blank" rel="nofollow"><strong>was $467 now $257 @ Amazon</strong></a><strong></strong></li><li><strong>Insta360 X4 Air: </strong><a href="https://www.amazon.com/Insta360-Starter-Bundle-Built-Stabilization/dp/B0FQNWDYMS" target="_blank" rel="nofollow"><strong>was $439 now $324 @ Amazon</strong></a><strong></strong></li><li><strong>DJI Osmo Pocket 3: </strong><a href="https://www.amazon.com/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD" target="_blank" rel="nofollow"><strong>was $499 now $378 @ Amazon</strong></a><strong></strong></li><li><strong>Insta360 X5: </strong><a href="https://www.amazon.com/Insta360-Waterproof-Replaceable-Built-Stabilization/dp/B0DZCBYCNY" target="_blank" rel="nofollow"><strong>was $549 now $434 @ Amazon</strong></a><strong></strong></li></ul><h2 class="article-body__section" id="section-dji-deals"><span>DJI deals</span></h2><div class="product"><a data-dimension112="6997216e-09d8-4d30-840f-ac0eddd0a41d" data-action="Deal Block" data-label="DJI Osmo Pocket 4" data-dimension48="DJI Osmo Pocket 4" data-dimension25="$378" href="https://www.amazon.com/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1440px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="vmxWbj6M4Ne3zJqUM3GmJF" name="Osmo Pocket 3" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/vmxWbj6M4Ne3zJqUM3GmJF.jpg" mos="" align="middle" fullscreen="" width="1440" height="1440" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Unfortunately, the latest <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review" target="_blank" data-dimension112="6997216e-09d8-4d30-840f-ac0eddd0a41d" data-action="Deal Block" data-label="DJI Osmo Pocket 4" data-dimension48="DJI Osmo Pocket 4" data-dimension25="$378">DJI Osmo Pocket 4</a> isn't available in the U.S., but thankfully the <a href="https://www.tomsguide.com/reviews/dji-osmo-pocket-3" target="_blank">Pocket 3</a> is still a fantastic camera. I've reviewed both, and the Pocket 3 is still a more than capable tool. Its key feature is the gimbal which keeps your footage super stable and smooth. It's an essential tool if you want to upgrade the look of your social or YouTube content.<a class="view-deal button" href="https://www.amazon.com/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD" target="_blank" rel="nofollow" data-dimension112="6997216e-09d8-4d30-840f-ac0eddd0a41d" data-action="Deal Block" data-label="DJI Osmo Pocket 4" data-dimension48="DJI Osmo Pocket 4" data-dimension25="$378">View Deal</a></p></div><div class="product"><a data-dimension112="3758cafd-a155-4897-8b99-dd1e562bd566" data-action="Deal Block" data-label="DJI Osmo Action 5 Pro" data-dimension48="DJI Osmo Action 5 Pro" data-dimension25="$288" href="https://www.amazon.com/gp/aw/d/B0DS2DF5S5" 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:81.33%;"><img id="yuhEUh9SGzYYoDGQX6gPJT" name="DJI Osmo Action 5 Pro GRID" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yuhEUh9SGzYYoDGQX6gPJT.jpg" mos="" align="middle" fullscreen="" width="1500" height="1220" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-5-pro-review" target="_blank" data-dimension112="3758cafd-a155-4897-8b99-dd1e562bd566" data-action="Deal Block" data-label="DJI Osmo Action 5 Pro" data-dimension48="DJI Osmo Action 5 Pro" data-dimension25="$288">DJI Osmo Action 5 Pro</a> held the top spot on my best action cameras list for a decent stretch of time (until it was succeeded by the newer <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-6-review" target="_blank">Osmo Action 6</a>). This camera is fantastic, and is still one of the best value-for-money propositions for action or motovlogging content. The video looks great, the stabilization is on point, there's internal storage, and it's as rugged as they come.<a class="view-deal button" href="https://www.amazon.com/gp/aw/d/B0DS2DF5S5" target="_blank" rel="nofollow" data-dimension112="3758cafd-a155-4897-8b99-dd1e562bd566" data-action="Deal Block" data-label="DJI Osmo Action 5 Pro" data-dimension48="DJI Osmo Action 5 Pro" data-dimension25="$288">View Deal</a></p></div><div class="product"><a data-dimension112="63f34e30-7137-43b6-a983-e1012327a093" data-action="Deal Block" data-label="Osmo Action 4" data-dimension48="Osmo Action 4" data-dimension25="$179" href="https://www.amazon.com/DJI-Waterproof-Low-Light-Performance-Long-Lasting/dp/B0C783YNW7" 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="8RNoiAvEdbtYEJ6r9mu2EA" name="DJI Osmo Action 4" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8RNoiAvEdbtYEJ6r9mu2EA.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you're after a budget powerhouse, the <a href="https://www.tomsguide.com/reviews/dji-osmo-action-4" target="_blank" data-dimension112="63f34e30-7137-43b6-a983-e1012327a093" data-action="Deal Block" data-label="Osmo Action 4" data-dimension48="Osmo Action 4" data-dimension25="$179">Osmo Action 4</a> is the camera for you. It's a couple of years old now, and DJI has made some serious progress in the later models, but this is still an excellent camera if you need something waterproof and rugged that'll output professional-looking 4K. And at this price, it's a no-brainer.<a class="view-deal button" href="https://www.amazon.com/DJI-Waterproof-Low-Light-Performance-Long-Lasting/dp/B0C783YNW7" target="_blank" rel="nofollow" data-dimension112="63f34e30-7137-43b6-a983-e1012327a093" data-action="Deal Block" data-label="Osmo Action 4" data-dimension48="Osmo Action 4" data-dimension25="$179">View Deal</a></p></div><div class="product"><a data-dimension112="8e737159-0b9f-4d1f-84cf-aaf828607e74" data-action="Deal Block" data-label="DJI Osmo 360" data-dimension48="DJI Osmo 360" data-dimension25="$357.47" href="https://www.amazon.com/DJI-Standard-Imaging-Recording-Waterproof/dp/B0DTTZZ6Q3/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1090px;"><p class="vanilla-image-block" style="padding-top:137.61%;"><img id="yUXRL4qFis7QUzehxiNGTN" name="DJI Osmo 360" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yUXRL4qFis7QUzehxiNGTN.jpg" mos="" align="middle" fullscreen="" width="1090" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>DJI's first 360 camera didn't do quite enough to match its rivals, the Insta360 X5 and GoPro MAX2. However, the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-360-review" target="_blank" data-dimension112="8e737159-0b9f-4d1f-84cf-aaf828607e74" data-action="Deal Block" data-label="DJI Osmo 360" data-dimension48="DJI Osmo 360" data-dimension25="$357.47">DJI Osmo 360</a> is still a perfectly capable 8K 360-degree camera, and is the one to go for if you're already invested in the DJI ecosystem and want the extra flexibility of 360 recording. It uses the same batteries as other Osmo cameras and can sync wirelessly to DJI mics.<a class="view-deal button" href="https://www.amazon.com/DJI-Standard-Imaging-Recording-Waterproof/dp/B0DTTZZ6Q3/" target="_blank" rel="nofollow" data-dimension112="8e737159-0b9f-4d1f-84cf-aaf828607e74" data-action="Deal Block" data-label="DJI Osmo 360" data-dimension48="DJI Osmo 360" data-dimension25="$357.47">View Deal</a></p></div><h2 class="article-body__section" id="section-gopro-deals"><span>GoPro deals</span></h2><div class="product"><a data-dimension112="0f9ebe9e-1df1-4dfd-9807-43fc17eaec16" data-action="Deal Block" data-label="MAX2" data-dimension48="MAX2" data-dimension25="$299" href="https://www.amazon.com/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FNGK6JZP" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1159px;"><p class="vanilla-image-block" style="padding-top:109.84%;"><img id="CZJ2XozEEfGMiunpAjNVFk" name="GoPro MAX2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/CZJ2XozEEfGMiunpAjNVFk.jpg" mos="" align="middle" fullscreen="" width="1159" height="1273" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This is my favorite deal so far this Prime Day. The <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/gopro-max2-review" target="_blank" data-dimension112="0f9ebe9e-1df1-4dfd-9807-43fc17eaec16" data-action="Deal Block" data-label="MAX2" data-dimension48="MAX2" data-dimension25="$299">MAX2</a> is an awesome 360 camera, and the closest rival to Insta360's X5. It loses out to the X5 on internal audio quality, but has it matched virtually everywhere else, including 8K video (where arguably, it's stronger). This $200 discount is frankly crazy. Unless you're invested in Insta360 or DJI systems already, snap this deal up now!<a class="view-deal button" href="https://www.amazon.com/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FNGK6JZP" target="_blank" rel="nofollow" data-dimension112="0f9ebe9e-1df1-4dfd-9807-43fc17eaec16" data-action="Deal Block" data-label="MAX2" data-dimension48="MAX2" data-dimension25="$299">View Deal</a></p></div><div class="product"><a data-dimension112="65eabcd6-6738-4c63-83b0-58b051e0420c" data-action="Deal Block" data-label="There aren't many decent deals on GoPro gear yet, and this is the only other one I'd recommend. The tiny LIT Hero shoots 4K/60p, offers decent battery life and thermals and has a built-in light. For similar money, I would recommend the DJI Osmo Action 4 instead, unless you're already invested in other GoPro gear — in which case, this would be my recommendation for a cheap action cam." data-dimension48="There aren't many decent deals on GoPro gear yet, and this is the only other one I'd recommend. The tiny LIT Hero shoots 4K/60p, offers decent battery life and thermals and has a built-in light. For similar money, I would recommend the DJI Osmo Action 4 instead, unless you're already invested in other GoPro gear — in which case, this would be my recommendation for a cheap action cam." data-dimension25="$179" href="https://www.amazon.com/GoPro-LIT-Hero-Built-Stabilization/dp/B0FP5R9BDZ" 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="jmQXkKy9zgwvpkYY52NRyn" name="GoPro LIT Hero deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/jmQXkKy9zgwvpkYY52NRyn.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>There aren't many decent deals on GoPro gear yet, and this is the only other one I'd recommend. The tiny LIT Hero shoots 4K/60p, offers decent battery life and thermals and has a built-in light. For similar money, I would recommend the DJI Osmo Action 4 instead, unless you're already invested in other GoPro gear — in which case, this would be my recommendation for a cheap action cam.<a class="view-deal button" href="https://www.amazon.com/GoPro-LIT-Hero-Built-Stabilization/dp/B0FP5R9BDZ" target="_blank" rel="nofollow" data-dimension112="65eabcd6-6738-4c63-83b0-58b051e0420c" data-action="Deal Block" data-label="There aren't many decent deals on GoPro gear yet, and this is the only other one I'd recommend. The tiny LIT Hero shoots 4K/60p, offers decent battery life and thermals and has a built-in light. For similar money, I would recommend the DJI Osmo Action 4 instead, unless you're already invested in other GoPro gear — in which case, this would be my recommendation for a cheap action cam." data-dimension48="There aren't many decent deals on GoPro gear yet, and this is the only other one I'd recommend. The tiny LIT Hero shoots 4K/60p, offers decent battery life and thermals and has a built-in light. For similar money, I would recommend the DJI Osmo Action 4 instead, unless you're already invested in other GoPro gear — in which case, this would be my recommendation for a cheap action cam." data-dimension25="$179">View Deal</a></p></div><h2 class="article-body__section" id="section-insta360-deals"><span>Insta360 deals</span></h2><div class="product"><a data-dimension112="dd1032aa-1386-49a2-a3dd-bc9a5a1e9279" data-action="Deal Block" data-label="X5" data-dimension48="X5" data-dimension25="$434.99" href="https://www.amazon.com/Insta360-Waterproof-Replaceable-Built-Stabilization/dp/B0DZCBYCNY" 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:111.40%;"><img id="yGDrsxDPAa6hgWeYP9bM5h" name="Insta360 X5" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yGDrsxDPAa6hgWeYP9bM5h.jpg" mos="" align="middle" fullscreen="" width="500" height="557" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Insta360 rarely offers huge discounts on its flagship products. However, the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-x5-review" target="_blank" data-dimension112="dd1032aa-1386-49a2-a3dd-bc9a5a1e9279" data-action="Deal Block" data-label="X5" data-dimension48="X5" data-dimension25="$434.99">X5</a> is getting a little long in the tooth now (at least by Insta360 standards...), so despite <em>still</em> being the best 360 camera around, I'm not surprised it's getting a chunk off the price now. Listen, the GoPro MAX2 is cheaper right now, making it a better choice in value-for-money terms. But if you want the <em>very</em> best camera and have the extra cash, this deal is for you.<a class="view-deal button" href="https://www.amazon.com/Insta360-Waterproof-Replaceable-Built-Stabilization/dp/B0DZCBYCNY" target="_blank" rel="nofollow" data-dimension112="dd1032aa-1386-49a2-a3dd-bc9a5a1e9279" data-action="Deal Block" data-label="X5" data-dimension48="X5" data-dimension25="$434.99">View Deal</a></p></div><div class="product"><a data-dimension112="25600d4c-08ba-4f52-a223-630bec942f63" data-action="Deal Block" data-label="The Insta360 X4 Air is the light version of the original X4, packing 8K/30p 360 footage, strong internal audio and Insta360's usual 'straight to social' mentality. This is a decent deal, but you can grab the GoPro MAX2 for $25 less right now, and that shoots 8K in 60fps, giving you more flexibility with fast-paced action footage (where these cameras are supposed to be used)." data-dimension48="The Insta360 X4 Air is the light version of the original X4, packing 8K/30p 360 footage, strong internal audio and Insta360's usual 'straight to social' mentality. This is a decent deal, but you can grab the GoPro MAX2 for $25 less right now, and that shoots 8K in 60fps, giving you more flexibility with fast-paced action footage (where these cameras are supposed to be used)." data-dimension25="$324.99" href="https://www.amazon.com/Insta360-Starter-Bundle-Built-Stabilization/dp/B0FQNWDYMS" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1121px;"><p class="vanilla-image-block" style="padding-top:91.53%;"><img id="BPUBeUjqhrTY5s78xLuzHS" name="Insta360 X4 Air" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/BPUBeUjqhrTY5s78xLuzHS.jpg" mos="" align="middle" fullscreen="" width="1121" height="1026" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Insta360 X4 Air is the light version of the original X4, packing 8K/30p 360 footage, strong internal audio and Insta360's usual 'straight to social' mentality. This is a decent deal, but you can grab the GoPro MAX2 for $25 less right now, and that shoots 8K in 60fps, giving you more flexibility with fast-paced action footage (where these cameras are supposed to be used). <a class="view-deal button" href="https://www.amazon.com/Insta360-Starter-Bundle-Built-Stabilization/dp/B0FQNWDYMS" target="_blank" rel="nofollow" data-dimension112="25600d4c-08ba-4f52-a223-630bec942f63" data-action="Deal Block" data-label="The Insta360 X4 Air is the light version of the original X4, packing 8K/30p 360 footage, strong internal audio and Insta360's usual 'straight to social' mentality. This is a decent deal, but you can grab the GoPro MAX2 for $25 less right now, and that shoots 8K in 60fps, giving you more flexibility with fast-paced action footage (where these cameras are supposed to be used)." data-dimension48="The Insta360 X4 Air is the light version of the original X4, packing 8K/30p 360 footage, strong internal audio and Insta360's usual 'straight to social' mentality. This is a decent deal, but you can grab the GoPro MAX2 for $25 less right now, and that shoots 8K in 60fps, giving you more flexibility with fast-paced action footage (where these cameras are supposed to be used)." data-dimension25="$324.99">View Deal</a></p></div><div class="product"><a data-dimension112="e088da30-110e-441b-b665-4b4f3c6f527a" data-action="Deal Block" data-label="360 on a budget! If you're not fussed about 4K exports, the X3 is still a fairly capable 360 camera. If you're mainly shooting for social, its 5.7K 360 resolution should be more than enough for either 1080p or 2.7K exports." data-dimension48="360 on a budget! If you're not fussed about 4K exports, the X3 is still a fairly capable 360 camera. If you're mainly shooting for social, its 5.7K 360 resolution should be more than enough for either 1080p or 2.7K exports." data-dimension25="$229.99" href="https://www.amazon.com/Insta360-Single-Lens-Stabilization-Touchscreen-Wintersports/dp/B0B9H572LC" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:859px;"><p class="vanilla-image-block" style="padding-top:145.17%;"><img id="Mm4ed6xKJUTATehFvdbfGJ" name="Insta360 X3" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Mm4ed6xKJUTATehFvdbfGJ.jpg" mos="" align="middle" fullscreen="" width="859" height="1247" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>360 on a budget! If you're not fussed about 4K exports, the X3 is still a fairly capable 360 camera. If you're mainly shooting for social, its 5.7K 360 resolution should be more than enough for either 1080p or 2.7K exports. <a class="view-deal button" href="https://www.amazon.com/Insta360-Single-Lens-Stabilization-Touchscreen-Wintersports/dp/B0B9H572LC" target="_blank" rel="nofollow" data-dimension112="e088da30-110e-441b-b665-4b4f3c6f527a" data-action="Deal Block" data-label="360 on a budget! If you're not fussed about 4K exports, the X3 is still a fairly capable 360 camera. If you're mainly shooting for social, its 5.7K 360 resolution should be more than enough for either 1080p or 2.7K exports." data-dimension48="360 on a budget! If you're not fussed about 4K exports, the X3 is still a fairly capable 360 camera. If you're mainly shooting for social, its 5.7K 360 resolution should be more than enough for either 1080p or 2.7K exports." data-dimension25="$229.99">View Deal</a></p></div><div class="product"><a data-dimension112="7bf5c1fa-01bb-404a-b772-c43209da3822" data-action="Deal Block" data-label="Ace Pro 2" data-dimension48="Ace Pro 2" data-dimension25="$309.99" href="https://www.amazon.com/Insta360-Dual-Battery-Bundle-Co-Engineered/dp/B0FLQ82JZK" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:944px;"><p class="vanilla-image-block" style="padding-top:99.89%;"><img id="4dhhwV4RqKuuSRRijbFpeD" name="Insta360 Ace Pro 2" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/4dhhwV4RqKuuSRRijbFpeD.jpg" mos="" align="middle" fullscreen="" width="944" height="943" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Ace Pro line has always been the bridesmaid and never the bride, losing out to the DJI and GoPro flagships each iteration. Still, the Ace Pro 2 is a formidable camera with a trick up its sleeve: 8K. This makes the Ace Pro 2 great if you need extra resolution for cropping and reframing in post, while maintaining high res exports. The <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-ace-pro-2-review" target="_blank" data-dimension112="7bf5c1fa-01bb-404a-b772-c43209da3822" data-action="Deal Block" data-label="Ace Pro 2" data-dimension48="Ace Pro 2" data-dimension25="$309.99">Ace Pro 2</a> hasn't dropped much in price over its current lifespan, until now! This is the lowest price ever.<a class="view-deal button" href="https://www.amazon.com/Insta360-Dual-Battery-Bundle-Co-Engineered/dp/B0FLQ82JZK" target="_blank" rel="nofollow" data-dimension112="7bf5c1fa-01bb-404a-b772-c43209da3822" data-action="Deal Block" data-label="Ace Pro 2" data-dimension48="Ace Pro 2" data-dimension25="$309.99">View Deal</a></p></div><h2 class="article-body__section" id="section-u-k-deals"><span>U.K. deals</span></h2><div class="product"><a data-dimension112="2b3d4886-ead8-4dce-ba0a-7b2213fdac3b" data-action="Deal Block" data-label="In the U.K., where DJI isn't subjected to the same regulations as the U.S., the Osmo Pocket 3 is much cheaper. The Pocket 4 is currently not on sale, so to any vloggers looking for a quick and effective upgrade to shooting on their smartphone: this is the deal for you!" data-dimension48="In the U.K., where DJI isn't subjected to the same regulations as the U.S., the Osmo Pocket 3 is much cheaper. The Pocket 4 is currently not on sale, so to any vloggers looking for a quick and effective upgrade to shooting on their smartphone: this is the deal for you!" data-dimension25="£277" href="https://www.amazon.co.uk/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1440px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="vmxWbj6M4Ne3zJqUM3GmJF" name="Osmo Pocket 3" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/vmxWbj6M4Ne3zJqUM3GmJF.jpg" mos="" align="middle" fullscreen="" width="1440" height="1440" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In the U.K., where DJI isn't subjected to the same regulations as the U.S., the Osmo Pocket 3 is much cheaper. The Pocket 4 is currently not on sale, so to any vloggers looking for a quick and effective upgrade to shooting on their smartphone: this is the deal for you!<a class="view-deal button" href="https://www.amazon.co.uk/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD/" target="_blank" rel="nofollow" data-dimension112="2b3d4886-ead8-4dce-ba0a-7b2213fdac3b" data-action="Deal Block" data-label="In the U.K., where DJI isn't subjected to the same regulations as the U.S., the Osmo Pocket 3 is much cheaper. The Pocket 4 is currently not on sale, so to any vloggers looking for a quick and effective upgrade to shooting on their smartphone: this is the deal for you!" data-dimension48="In the U.K., where DJI isn't subjected to the same regulations as the U.S., the Osmo Pocket 3 is much cheaper. The Pocket 4 is currently not on sale, so to any vloggers looking for a quick and effective upgrade to shooting on their smartphone: this is the deal for you!" data-dimension25="£277">View Deal</a></p></div><div class="product"><a data-dimension112="f6cf2a2c-e32f-4d7e-a949-3703ac249262" data-action="Deal Block" data-label="The Osmo Action 5 Pro is now extremely keenly priced in the U.K., at just £233. This puts it into the budget action camera realm, which is frankly astounding given this is only last year's flagship model, and is a capable professional tool!" data-dimension48="The Osmo Action 5 Pro is now extremely keenly priced in the U.K., at just £233. This puts it into the budget action camera realm, which is frankly astounding given this is only last year's flagship model, and is a capable professional tool!" data-dimension25="£233" href="https://www.amazon.co.uk/DJI-Standard-Waterproof-Stabilization-Touchscreens/dp/B0D1YH4B2R" 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:81.33%;"><img id="yuhEUh9SGzYYoDGQX6gPJT" name="DJI Osmo Action 5 Pro GRID" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yuhEUh9SGzYYoDGQX6gPJT.jpg" mos="" align="middle" fullscreen="" width="1500" height="1220" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Osmo Action 5 Pro is now extremely keenly priced in the U.K., at just £233. This puts it into the budget action camera realm, which is frankly astounding given this is only last year's flagship model, and is a capable professional tool!<a class="view-deal button" href="https://www.amazon.co.uk/DJI-Standard-Waterproof-Stabilization-Touchscreens/dp/B0D1YH4B2R" target="_blank" rel="nofollow" data-dimension112="f6cf2a2c-e32f-4d7e-a949-3703ac249262" data-action="Deal Block" data-label="The Osmo Action 5 Pro is now extremely keenly priced in the U.K., at just £233. This puts it into the budget action camera realm, which is frankly astounding given this is only last year's flagship model, and is a capable professional tool!" data-dimension48="The Osmo Action 5 Pro is now extremely keenly priced in the U.K., at just £233. This puts it into the budget action camera realm, which is frankly astounding given this is only last year's flagship model, and is a capable professional tool!" data-dimension25="£233">View Deal</a></p></div><div class="product"><a data-dimension112="e97e8055-86f3-4a5a-a3cb-add013174b7a" data-action="Deal Block" data-label="In both the U.S. and U.K. markets, the Osmo Action 4 is a no brainer if you're on a budget. This really is as cheap as respectable action cameras get. So, while you might find a GoPro Hero, an older used camera, or a disreputable brand cheaper, there really is no reason to buy any of them when the OA4 is just £159. Crazy cheap." data-dimension48="In both the U.S. and U.K. markets, the Osmo Action 4 is a no brainer if you're on a budget. This really is as cheap as respectable action cameras get. So, while you might find a GoPro Hero, an older used camera, or a disreputable brand cheaper, there really is no reason to buy any of them when the OA4 is just £159. Crazy cheap." data-dimension25="£159" href="https://www.amazon.co.uk/DJI-Essential-Low-Light-Underwater-Performance/dp/B0DS2B3P2B" 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="8RNoiAvEdbtYEJ6r9mu2EA" name="DJI Osmo Action 4" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8RNoiAvEdbtYEJ6r9mu2EA.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In both the U.S. and U.K. markets, the Osmo Action 4 is a no brainer if you're on a budget. This really is as cheap as respectable action cameras get. So, while you might find a GoPro Hero, an older used camera, or a disreputable brand cheaper, there really is no reason to buy any of them when the OA4 is just £159. Crazy cheap.<a class="view-deal button" href="https://www.amazon.co.uk/DJI-Essential-Low-Light-Underwater-Performance/dp/B0DS2B3P2B" target="_blank" rel="nofollow" data-dimension112="e97e8055-86f3-4a5a-a3cb-add013174b7a" data-action="Deal Block" data-label="In both the U.S. and U.K. markets, the Osmo Action 4 is a no brainer if you're on a budget. This really is as cheap as respectable action cameras get. So, while you might find a GoPro Hero, an older used camera, or a disreputable brand cheaper, there really is no reason to buy any of them when the OA4 is just £159. Crazy cheap." data-dimension48="In both the U.S. and U.K. markets, the Osmo Action 4 is a no brainer if you're on a budget. This really is as cheap as respectable action cameras get. So, while you might find a GoPro Hero, an older used camera, or a disreputable brand cheaper, there really is no reason to buy any of them when the OA4 is just £159. Crazy cheap." data-dimension25="£159">View Deal</a></p></div><div class="product"><a data-dimension112="9f68df8f-7d80-44b3-b61e-392877adc75d" data-action="Deal Block" data-label="DJI Osmo Nano" data-dimension48="DJI Osmo Nano" data-dimension25="£223.25" href="https://www.amazon.co.uk/DJI-Osmo-Nano-Standard-Combo/dp/B0FBFZM45S" 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="A7J8t2TnCNTfKnsaCkbtvR" name="dji-osmo-nano-standard-combo-128gb--smal-f9aa7961-ac4a-4fb4-9812-a50344a4bdd2.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/A7J8t2TnCNTfKnsaCkbtvR.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-nano-review" target="_blank" data-dimension112="9f68df8f-7d80-44b3-b61e-392877adc75d" data-action="Deal Block" data-label="DJI Osmo Nano" data-dimension48="DJI Osmo Nano" data-dimension25="£223.25">DJI Osmo Nano</a> is DJI's answer to the Insta360 Go and GoPro Hero lines of tiny action cams. And it's great! It features a removable screen which you can use as a remote and monitor. Fun fact: I shot my own wedding reception with one of these strapped to my head. Check out my review linked above to see some of that footage.<a class="view-deal button" href="https://www.amazon.co.uk/DJI-Osmo-Nano-Standard-Combo/dp/B0FBFZM45S" target="_blank" rel="nofollow" data-dimension112="9f68df8f-7d80-44b3-b61e-392877adc75d" data-action="Deal Block" data-label="DJI Osmo Nano" data-dimension48="DJI Osmo Nano" data-dimension25="£223.25">View Deal</a></p></div><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-deals"><span>We're tracking all the best Prime Day deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Cameras+%26+Photography&view_mode=savings_squad&widget_title=Top+Prime+Day+camera+%26+photography+deals&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');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          const topBarDiv = this.root.querySelector('#tg-df-top-bar');          const headerElement = this.root.querySelector('#tg-df-savings-squad-header');          if (headerElement) {             headerElement.style.display = mode === 'savings_squad' ? 'block' : 'none';          }          if (mode === 'carousel') {             this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (topBarDiv) topBarDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.add('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (topBarDiv) topBarDiv.style.display = 'block';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!this.viewModeOverride && initialViewMode) {            this.viewModeOverride = initialViewMode;          }          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Gaming laptops';          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (this.viewModeSelect && params.has('view_mode')) {            this.viewModeSelect.value = params.get('view_mode');          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'date_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }          const headerTitleEl = this.root.querySelector('#tg-df-header-title');          const headerSubtitleEl = this.root.querySelector('#tg-df-header-subtitle');          if (params.has('widget_title') && headerTitleEl) {             headerTitleEl.textContent = params.get('widget_title');          }          if (params.has('widget_subtitle') && headerSubtitleEl) {             headerSubtitleEl.textContent = params.get('widget_subtitle');          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            if (this.getViewMode() === 'savings_squad') {              this.searchInput.value = '';            } else {              this.searchInput.value = this.currentQuery;            }          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'date_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const handleFiltersScroll = () => {            const filters = this.root.querySelector('.tg-df-filters');            const leftBtn = this.root.querySelector('.tg-df-scroll-btn.left');            const rightBtn = this.root.querySelector('.tg-df-scroll-btn.right');            if (filters && leftBtn && rightBtn) {              const { scrollLeft, scrollWidth, clientWidth } = filters;              leftBtn.style.display = scrollLeft > 0 ? 'flex' : 'none';              rightBtn.style.display = Math.ceil(scrollLeft + clientWidth) < scrollWidth - 5 ? 'flex' : 'none';            }          };          const filters = this.root.querySelector('.tg-df-filters');          if (filters) {            filters.addEventListener('scroll', handleFiltersScroll);            window.addEventListener('resize', handleFiltersScroll);            setTimeout(handleFiltersScroll, 100);                        // Also call after rendering dropdowns            const origRenderCategories = this.renderCategories;            if (origRenderCategories) {               this.renderCategories = (...args) => {                 origRenderCategories.apply(this, args);                 setTimeout(handleFiltersScroll, 50);               };            }          }                const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          const showAutocomplete = () => {             if (this.getViewMode() !== 'savings_squad' || !this.autocompleteDropdown || !this.airedaleTags) return;                          let terms = this.airedaleTags;             if (this.airedaleBrands) {                terms = terms.concat(this.airedaleBrands.map(b => b.formatted_value));             }             terms = [...new Set(terms)];                          const query = this.searchInput.value.trim();             let matches = [];             if (query.length > 0) {                 matches = terms.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase());             } else {                 matches = terms;             }                          if (matches.length > 0) {                 this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                 this.autocompleteDropdown.classList.add('active');             } else {                 this.autocompleteDropdown.classList.remove('active');             }          };          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('focus', showAutocomplete);            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              showAutocomplete();              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  if (this.getViewMode() === 'savings_squad') {                    this.activeDealTag = null;                    this.currentQuery = '';                    if (this.categoryFilter) this.categoryFilter.value = 'all';                    this.fetchDeals('');                  } else {                    this.deals = [];                    this.render();                  }                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                                let isTag = false;                if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;                if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;                this.activeDealTag = isTag ? query : null;                                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   if (query.length === 0 && this.getViewMode() === 'savings_squad') {                       if (this.categoryFilter) this.categoryFilter.value = 'all';                   }                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   if (this.categoryFilter && this.airedaleTags.includes(tag)) {                       this.categoryFilter.value = tag;                   }                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                            let isTag = false;              if (this.airedaleTags && this.airedaleTags.includes(query)) isTag = true;              if (this.airedaleBrands && this.airedaleBrands.some(b => b.formatted_value === query)) isTag = true;              this.activeDealTag = isTag ? query : null;                            this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 if (query.length === 0 && this.getViewMode() === 'savings_squad') {                     if (this.categoryFilter) this.categoryFilter.value = 'all';                 }                 this.fetchDeals(query);              }            });          }          if(this.sortSelect && this.sortSelect.querySelector('option[value="date_desc"]') === null) {              const option = document.createElement('option');              option.value = "date_desc";              option.text = "Newest First";              this.sortSelect.insertBefore(option, this.sortSelect.firstChild);          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               if (val) {                 this.currentQuery = val;               } else {                 if (this.searchInput && this.currentQuery === document.querySelector('#tg-df-brand-trigger')?.getAttribute('data-active-brand')) {                     // don't clear current query if a brand is selected                 } else if (this.searchInput) {                     this.currentQuery = '';                     this.searchInput.value = '';                 }               }               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = 'date_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }        async fetchDeals(query) {          this.showLoading();          this.deals = [];          this.displayLimit = (this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;                    try {            console.log("getViewMode returns:", this.getViewMode());            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad();            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query);               } else {                 await this.fetchHawkDeals(query);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (!this.activeDealTag && this.currentQuery) {             const tagMatch = this.airedaleTags.find(t => t.toLowerCase() === this.currentQuery.toLowerCase());             if (tagMatch) {                this.activeDealTag = tagMatch;             }          }          if (this.activeDealTag) {             const cleanTag = this.activeDealTag.toLowerCase().replace(/&/g, '').replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');             const encodedTag = encodeURIComponent(cleanTag);             const url = `https://airedale.futurecdn.net/feeds/feed_1781000519267.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let seenUrls = new Set();                    let overallBrandsCounts = {};                    // First pass: extract ALL brands from topArticles so the dropdown has all options          topArticles.forEach((article) => {             if (!article.articlepage) return;             let pageData = [];             try { pageData = JSON.parse(article.articlepage[0]); } catch(e){ console.warn(e); }             const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');             savingsSquad.forEach((block) => {                const data = block.data || {};                if (data.brand) {                   const cleanBrand = data.brand.replace(/^\d+\.\s*/, '').trim();                   overallBrandsCounts[cleanBrand] = (overallBrandsCounts[cleanBrand] || 0) + 1;                }             });          });          targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                if (externalUrl) {                   if (seenUrls.has(externalUrl)) return;                  seenUrls.add(externalUrl);                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || '',                   authorName: article.articleauthortext ? article.articleauthortext[0] : (article.articleauthor ? article.articleauthor[0] : ''),                   authorRole: article.articleauthorrole ? article.articleauthorrole[0] : '',                   authorImage: article.articleauthormedia ? article.articleauthormedia[0] : '',                   documentUrl: article.documenturl ? article.documenturl[0] : '',                   modifiedDate: article.contentmodifieddate || article.modifieddate || ''                });             });          });                    const airedaleBrandsList = Object.keys(overallBrandsCounts).map(b => ({              formatted_value: b,              count: overallBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    if (this.rowsSelect && this.rowsSelect.value) {            url.searchParams.append('rows', this.rowsSelect.value);          } else {             url.searchParams.append('rows', '12'); // default          }          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = 12;          if (this.rowsSelect && this.rowsSelect.value) {            rowLimit = parseInt(this.rowsSelect.value, 10) || 12;          }          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : 'date_desc';          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                ${_div}              ${_div}              \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin:0; border-radius:0;">${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)}${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = 'View Deal';              if (isSavingsSquadMode) {                priceGroupHtml = ``;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = isSavingsSquadMode ? `` : `                \x3Cdiv class="tg-df-card-price-group">                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                          \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                          \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                            var c = this.parentNode;                            if (this.dataset.expanded === 'true') {                              var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                              if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                              pd.style.display = '-webkit-box';                              pd.style.webkitLineClamp = '3';                              this.textContent = 'READ MORE';                              this.style.position = 'absolute';                              this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                              this.style.paddingLeft = '16px';                              this.dataset.expanded = 'false';                            } else {                              var pd = this.previousElementSibling;                              pd.style.display = 'inline';                              pd.style.webkitLineClamp = 'unset';                              this.textContent = 'READ LESS';                              this.style.position = 'static';                              this.style.background = 'transparent';                              this.style.paddingLeft = '4px';                              this.dataset.expanded = 'true';                              pd.appendChild(this);                            }                          ">READ MORE${_button}                        \x3C/div>` : ''}                      ${_div}                    ${_div}                    ${deal.authorName ? `                      \x3Cdiv class="tg-df-author-line-mobile" style="padding: 0 0 12px 0; background: transparent;">                         \x3Cdiv style="display: flex; align-items: center; gap: 12px;">                            ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="40" height="40" style="border-radius: 50%; object-fit: cover;">` : ''}                            \x3Cdiv style="display: flex; flex-direction: column;">                               \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">\x3Cspan style="color: #FF6600;">${this.escapeHTML(deal.merchant)}${_span} deal recommended by:${_div}                               \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.3;">                                  \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                  ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                  ${deal.modifiedDate ? `\x3Cdiv style="color: var(--tg-df-text-muted); margin-top: 2px;">${getTimeAgo(deal.modifiedDate)}${_div}` : ''}                               ${_div}                            ${_div}                         ${_div}                      ${_div}                    ` : ''}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>View Deal${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                  \x3Cdiv class="tg-df-card-merchant-wrapper" style="position: absolute; bottom: 0; right: 0; background: transparent; padding: 8px 12px; z-index: 10;">                     \x3Cspan class="tg-df-card-merchant-pill" style="text-align: right; margin-bottom: 0;" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cdiv class="tg-df-card-desc-container" style="margin-bottom: 12px; position: relative;">                    \x3Cp class="tg-df-card-desc-content" style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 0; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;">${this.escapeHTML(deal.description)}${_p}                    \x3Cbutton type="button" class="tg-df-card-desc-btn" style="display: none; appearance: none; border: none; color: #000000; font-size: 11px; font-weight: 700; text-transform: uppercase; cursor: pointer; font-family: inherit; position: absolute; bottom: 2px; right: 0; background: linear-gradient(to right, transparent, #fff 20%, #fff); padding: 0 0 0 16px;" onclick="                      var c = this.parentNode;                      if (this.dataset.expanded === 'true') {                        var pd = (c.tagName === 'P') ? c : this.previousElementSibling;                        if (c.tagName === 'P') { c.parentNode.appendChild(this); pd = c; }                        pd.style.display = '-webkit-box';                        pd.style.webkitLineClamp = '3';                        this.textContent = 'READ MORE';                        this.style.position = 'absolute';                        this.style.background = 'linear-gradient(to right, transparent, #fff 20%, #fff)';                        this.style.paddingLeft = '16px';                        this.dataset.expanded = 'false';                      } else {                        var pd = this.previousElementSibling;                        pd.style.display = 'inline';                        pd.style.webkitLineClamp = 'unset';                        this.textContent = 'READ LESS';                        this.style.position = 'static';                        this.style.background = 'transparent';                        this.style.paddingLeft = '4px';                        this.dataset.expanded = 'true';                        pd.appendChild(this);                      }                    ">READ MORE${_button}                  \x3C/div>` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${deal.authorName ? `                    \x3Cdiv class="tg-df-author-line-desktop" style="padding: 0 0 ${isSavingsSquadMode ? 0 : 12}px 0;">                       \x3Cdiv style="display: flex; align-items: center; gap: 10px;">                          ${deal.authorImage ? `\x3Cimg src="${this.escapeHTML(deal.authorImage)}" alt="${this.escapeHTML(deal.authorName)}" class="tg-df-author-img" width="36" height="36" style="border-radius: 50%; object-fit: cover;">` : ''}                          \x3Cdiv style="display: flex; flex-direction: column;">                             \x3Cdiv style="font-size: 10px; color: var(--tg-df-text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 2px; font-weight: 600;">Recommended by:${_div}                             \x3Cdiv style="font-size: 11px; color: var(--tg-df-text); line-height: 1.2;">                                \x3Cstrong>\x3Ca href="https://www.tomsguide.com/${this.escapeHTML(deal.documentUrl || '').replace(/^\/+/, '')}" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit; border-bottom: 1px dotted var(--tg-df-text-muted);">${this.escapeHTML(deal.authorName)}${_a}${_strong}                                ${deal.authorRole && !['null', 'nul', 'undefined'].includes(String(deal.authorRole).toLowerCase()) ? ` • ${this.escapeHTML(deal.authorRole)}` : ''}                                ${deal.modifiedDate ? `\x3Cspan style="color: var(--tg-df-text-muted);"> • ${getTimeAgo(deal.modifiedDate)}${_span}` : ''}                             ${_div}                          ${_div}                       ${_div}                    ${_div}                    ` : ''}                    ${priceGroupHtml}                  ${_div}                ${_div}                \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta" style="text-decoration: none; border-radius: 0;">${ctaText}${_a}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit) {            dealsHtml += `              \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer;">Load More${_button}              ${_div}            `;          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }          setTimeout(() => {            const contents = this.root.querySelectorAll('.tg-df-card-desc-content');            contents.forEach(p => {              if (p.scrollHeight > p.clientHeight || p.scrollHeight > 60) {                if (p.nextElementSibling) {                  p.nextElementSibling.style.display = 'block';                }              }            });                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');            if (!this.processArticleFired) {                  this.processArticleFired = true;                  document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));               }          }, 50);          const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              this.displayLimit += 12;              this.render();            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I review drones for a living and these are my 9 favorite affordable options for summer flying from DJI, Potensic and more ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/drones/amazon-prime-day-2026-drones-deals</link>
                                                                            <description>
                            <![CDATA[ I fly drones for a living so if you want to take up aerial photography this summer, I've got you covered. These are my favorite Prime Day drone deals this week. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">mMjnYGBtGP2RjWjD5jcKz8</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/vTY7zuepvjXbP8nKHZBuHm-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 23 Jun 2026 12:50:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Drones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/vTY7zuepvjXbP8nKHZBuHm-1280-80.jpg">
                                                            <media:credit><![CDATA[DJI / Potensic / HoverAir / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of drones]]></media:description>                                                            <media:text><![CDATA[A selection of drones]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of drones]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/vTY7zuepvjXbP8nKHZBuHm-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>This year's <a href="https://www.tomsguide.com/live/news/prime-day-2026-handpicked-editor-recommended-deals-live">Amazon Prime Day deals</a> are in full swing and that can only mean one thing: my favorite quadcopters have received massive price cuts so now's the time to save big on the <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">best drones.</a> Whether you're a veteran pilot or a newbie, there are plenty of discounts to be enjoyed by everyone — but there are so many deals to choose from, where do you even start?</p><p>I've got you so don't worry. I review drones for a living so I'm here to cut through the noise and bring you only the crème de la crème of deals. The best discount you can grab today is on the <a href="https://www.amazon.com/DJI-Mini-Fly-More-Combo/dp/B0BL3R3L45/" target="_blank" rel="nofollow">DJI Mini 3 Fly More Combo which is a whopping 38% off</a>, so it's available for just $449 — and it includes a controller, carry bag and extra batteries. Also, our favorite budget drone is even cheaper today, so you can get the <a href="https://www.amazon.com/DJI-QuickShots-Stabilized-Propeller-Controller-Free/dp/B07FTPX71F/" target="_blank" rel="nofollow">DJI Neo for as little as $139</a>!</p><p>Of course, it's not just DJI's quadcopters that are discounted. The deal I'm most enticed by is on the <a href="https://www.amazon.com/Antigravity-Infinity-Micro-OLED-Goggles-Assistant/dp/B0H38VHZFR/" target="_blank" rel="nofollow">Antigravity A1 Infinity Bundle which is down to its lowest-ever price of $1,599</a>. I know that still sounds like a lot but you're saving over $400 on one of the best 360° 8K drones you can buy today.</p><p>And there's plenty more where that came from. Here are the 9 best Prime Day drone deals I've found (so far).</p><h2 id="meet-your-drones-deals-expert">Meet your drones deals expert</h2><h2 class="article-body__section" id="section-quick-links"><span>Quick links</span></h2><ul><li><strong></strong><a href="https://www.amazon.com/primeday" target="_blank" rel="nofollow"><strong>Shop all Amazon Prime Day deals now</strong></a><strong></strong></li><li><strong>DJI Mini 3 Fly More Combo: </strong><a href="https://www.amazon.com/DJI-Mini-Fly-More-Combo/dp/B0BL3R3L45/" target="_blank" rel="nofollow"><strong>was $719 now $449</strong></a><strong></strong></li><li><strong>DJI Mini 4K Fly More Combo: </strong><a href="https://www.amazon.com/DJI-Stabilization-Transmission-Batteries-QuickShots/dp/B0F4WZ3Q2J/" target="_blank" rel="nofollow"><strong>was $389 now $255</strong></a><strong></strong></li><li><strong>DJI Neo: </strong><a href="https://www.amazon.com/DJI-QuickShots-Stabilized-Propeller-Controller-Free/dp/B07FTPX71F/" target="_blank" rel="nofollow"><strong>was $199 now $139</strong></a><strong></strong></li><li><strong>DJI Flip (with two batteries): </strong><a href="https://www.amazon.com/DJI-Battery-Controller-Intelligent-Batteries/dp/B0FR4HJQFR/" target="_blank" rel="nofollow"><strong>was $389 now $309</strong></a><strong></strong></li><li><strong>Antigravity A1 Infinity Bundle: </strong><a href="https://www.amazon.com/Antigravity-Infinity-Micro-OLED-Goggles-Assistant/dp/B0H38VHZFR/" target="_blank" rel="nofollow"><strong>was $1,999 now $1,599</strong></a><strong></strong></li><li><strong>Potensic Atom 2 Fly More Combo: </strong><a href="https://www.amazon.com/Potensic-Transmission-Vertical-Shooting-QuickShots/dp/B0DKHCZHCY/" target="_blank" rel="nofollow"><strong>was $479 now $324</strong></a><strong></strong></li><li><strong>Potensic Atom SE Expansion Kit: </strong><a href="https://www.amazon.com/gp/aw/d/B0BFWS4N4T/" target="_blank" rel="nofollow"><strong>was $229 now $195</strong></a><strong></strong></li><li><strong>HoverAir X1 Combo Plus: </strong><a href="https://www.amazon.com/HOVERAir-Self-Flying-Foldable-Intelligent-Hands-Free/dp/B0CQJTHM5B/" target="_blank" rel="nofollow"><strong>was $339 now $271</strong></a></li><li><strong>HoverAir X1 ProMax: </strong><a href="https://www.amazon.com/HOVERAir-Foldable-Automatic-OmniTerrain-SmoothCapture/dp/B0DGTHLVR3/" target="_blank" rel="nofollow"><strong>was $699 now $594</strong></a></li></ul><h2 class="article-body__section" id="section-best-prime-day-dji-drone-deals"><span>Best Prime Day DJI drone deals</span></h2><div class="product"><a data-dimension112="c99a048a-7dca-4e9b-8000-efb053c71ad4" data-action="Deal Block" data-label="DJI Mini 3" data-dimension48="DJI Mini 3" data-dimension25="$449" href="https://www.amazon.com/DJI-Mini-Fly-More-Combo/dp/B0BL3R3L45/" 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:76.40%;"><img id="dv4MemdBp94uLw7A4wUJLj" name="DJI-Mini-3-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/dv4MemdBp94uLw7A4wUJLj.jpg" mos="" align="middle" fullscreen="" width="1500" height="1146" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Though the <a href="https://www.tomsguide.com/reviews/dji-mini-3" data-dimension112="c99a048a-7dca-4e9b-8000-efb053c71ad4" data-action="Deal Block" data-label="DJI Mini 3" data-dimension48="DJI Mini 3" data-dimension25="$449">DJI Mini 3</a> is a couple generations old now, it's still a competent quadcopter. It packs a 1/1.3-inch CMOS sensor that's capable of capturing stunning, smooth 4K/30fps horizontal and vertical video as well as 12MP stills. 38 minutes of flight time packed into a compact body make it one of the best drones for beginners and those who love to travel.<a class="view-deal button" href="https://www.amazon.com/DJI-Mini-Fly-More-Combo/dp/B0BL3R3L45/" target="_blank" rel="nofollow" data-dimension112="c99a048a-7dca-4e9b-8000-efb053c71ad4" data-action="Deal Block" data-label="DJI Mini 3" data-dimension48="DJI Mini 3" data-dimension25="$449">View Deal</a></p></div><div class="product"><a data-dimension112="e5a9a14d-752b-40f2-8088-ab018e7bdf75" data-action="Deal Block" data-label="The DJI Mini 4K features some pro specs. Not only can you capture 4K/30fps footage, but you also get access to QuickShots and panoramas with a 31-minute flight time. The drone also features a smart return to home and stable hovering, making it the ideal drone for beginners and drone enthusiasts." data-dimension48="The DJI Mini 4K features some pro specs. Not only can you capture 4K/30fps footage, but you also get access to QuickShots and panoramas with a 31-minute flight time. The drone also features a smart return to home and stable hovering, making it the ideal drone for beginners and drone enthusiasts." data-dimension25="$255" href="https://www.amazon.com/DJI-Stabilization-Transmission-Batteries-QuickShots/dp/B0F4WZ3Q2J/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:92.20%;"><img id="PgvNvPxsvHpkPD86QX6xFj" name="DJI-Mini-4K-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/PgvNvPxsvHpkPD86QX6xFj.jpg" mos="" align="middle" fullscreen="" width="1500" height="1383" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The DJI Mini 4K features some pro specs. Not only can you capture 4K/30fps footage, but you also get access to QuickShots and panoramas with a 31-minute flight time. The drone also features a smart return to home and stable hovering, making it the ideal drone for beginners and drone enthusiasts.<a class="view-deal button" href="https://www.amazon.com/DJI-Stabilization-Transmission-Batteries-QuickShots/dp/B0F4WZ3Q2J/" target="_blank" rel="nofollow" data-dimension112="e5a9a14d-752b-40f2-8088-ab018e7bdf75" data-action="Deal Block" data-label="The DJI Mini 4K features some pro specs. Not only can you capture 4K/30fps footage, but you also get access to QuickShots and panoramas with a 31-minute flight time. The drone also features a smart return to home and stable hovering, making it the ideal drone for beginners and drone enthusiasts." data-dimension48="The DJI Mini 4K features some pro specs. Not only can you capture 4K/30fps footage, but you also get access to QuickShots and panoramas with a 31-minute flight time. The drone also features a smart return to home and stable hovering, making it the ideal drone for beginners and drone enthusiasts." data-dimension25="$255">View Deal</a></p></div><div class="product"><a data-dimension112="9adfbf4e-1483-4b81-9e99-27081197f5b9" data-action="Deal Block" data-label="DJI Neo review," data-dimension48="DJI Neo review," data-dimension25="$139" href="https://www.amazon.com/DJI-QuickShots-Stabilized-Propeller-Controller-Free/dp/B07FTPX71F/" 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="yQ2D9952vwuZALYFFAiFF6" name="Neo-dealbox" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/yQ2D9952vwuZALYFFAiFF6.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Even though it has now been succeeded by the DJI Neo 2, the Neo is still a beautiful little drone. In my <a href="https://www.tomsguide.com/cameras-photography/drones/dji-neo-review" data-dimension112="9adfbf4e-1483-4b81-9e99-27081197f5b9" data-action="Deal Block" data-label="DJI Neo review," data-dimension48="DJI Neo review," data-dimension25="$139">DJI Neo review,</a> I praised this drone for its lightweight, compact and minimalistic design. It can shoot 4K footage at 30fps, take detailed stills, and remain stable in tricky conditions. Easy to control with the RC-N3 controller or the DJI Fly app on a smartphone, the Neo makes drone photography more accessible thanks to its low price point.<a class="view-deal button" href="https://www.amazon.com/DJI-QuickShots-Stabilized-Propeller-Controller-Free/dp/B07FTPX71F/" target="_blank" rel="nofollow" data-dimension112="9adfbf4e-1483-4b81-9e99-27081197f5b9" data-action="Deal Block" data-label="DJI Neo review," data-dimension48="DJI Neo review," data-dimension25="$139">View Deal</a></p></div><div class="product"><a data-dimension112="e1697176-30c1-4550-b19d-ba597da548a8" data-action="Deal Block" data-label="DJI Flip review" data-dimension48="DJI Flip review" data-dimension25="$309" href="https://www.amazon.com/DJI-Battery-Controller-Intelligent-Batteries/dp/B0FR4HJQFR/" 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:70.20%;"><img id="wLTt4oR293UT4kynWhFF3A" name="DJI Flip deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/wLTt4oR293UT4kynWhFF3A.jpg" mos="" align="middle" fullscreen="" width="1500" height="1053" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In my <a href="https://www.tomsguide.com/cameras-photography/drones/dji-flip-review" data-dimension112="e1697176-30c1-4550-b19d-ba597da548a8" data-action="Deal Block" data-label="DJI Flip review" data-dimension48="DJI Flip review" data-dimension25="$309">DJI Flip review</a>, I noted that this drone was perfect for beginners or those looking to upgrade from older DJI models. The Flip captures detailed 48MP stills in all lighting conditions, and records gorgeous 4K/60fps footage. It’s great for content creators as it features many shooting modes, plus it’s built really well and sports a slick design — a little like a droid from Star Wars! The Flip is, in my opinion, the best drone for beginners.<a class="view-deal button" href="https://www.amazon.com/DJI-Battery-Controller-Intelligent-Batteries/dp/B0FR4HJQFR/" target="_blank" rel="nofollow" data-dimension112="e1697176-30c1-4550-b19d-ba597da548a8" data-action="Deal Block" data-label="DJI Flip review" data-dimension48="DJI Flip review" data-dimension25="$309">View Deal</a></p></div><h2 class="article-body__section" id="section-best-prime-day-antigravity-drone-deals"><span>Best Prime Day Antigravity drone deals</span></h2><div class="product"><a data-dimension112="cf859c35-fefa-427c-b021-4181e59e7ed5" data-action="Deal Block" data-label="For the first time, the Antigravity A1 Infinity Bundle is down to its lowest-ever price. As a 360° drone, the A1 lets you capture every angle possible. Footage is shot in 8K/30fps, enabling you to maintain 4K in 16x9 when reframing in post — and this footage is packed with detail. 55MP stills look great too. Flying the drone is easy (and immersive) with the Vision goggles and Grip controller." data-dimension48="For the first time, the Antigravity A1 Infinity Bundle is down to its lowest-ever price. As a 360° drone, the A1 lets you capture every angle possible. Footage is shot in 8K/30fps, enabling you to maintain 4K in 16x9 when reframing in post — and this footage is packed with detail. 55MP stills look great too. Flying the drone is easy (and immersive) with the Vision goggles and Grip controller." data-dimension25="$1599" href="https://www.amazon.com/Antigravity-Infinity-Micro-OLED-Goggles-Assistant/dp/B0H38VHZFR/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:884px;"><p class="vanilla-image-block" style="padding-top:69.12%;"><img id="bj8knFFm785m6wpHyMmgQT" name="Antigravity A1 deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/bj8knFFm785m6wpHyMmgQT.jpg" mos="" align="middle" fullscreen="" width="884" height="611" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>For the first time, the Antigravity A1 Infinity Bundle is down to its lowest-ever price. As a 360° drone, the A1 lets you capture every angle possible. Footage is shot in 8K/30fps, enabling you to maintain 4K in 16x9 when reframing in post — and this footage is packed with detail. 55MP stills look great too. Flying the drone is easy (and immersive) with the Vision goggles and Grip controller.<a class="view-deal button" href="https://www.amazon.com/Antigravity-Infinity-Micro-OLED-Goggles-Assistant/dp/B0H38VHZFR/" target="_blank" rel="nofollow" data-dimension112="cf859c35-fefa-427c-b021-4181e59e7ed5" data-action="Deal Block" data-label="For the first time, the Antigravity A1 Infinity Bundle is down to its lowest-ever price. As a 360° drone, the A1 lets you capture every angle possible. Footage is shot in 8K/30fps, enabling you to maintain 4K in 16x9 when reframing in post — and this footage is packed with detail. 55MP stills look great too. Flying the drone is easy (and immersive) with the Vision goggles and Grip controller." data-dimension48="For the first time, the Antigravity A1 Infinity Bundle is down to its lowest-ever price. As a 360° drone, the A1 lets you capture every angle possible. Footage is shot in 8K/30fps, enabling you to maintain 4K in 16x9 when reframing in post — and this footage is packed with detail. 55MP stills look great too. Flying the drone is easy (and immersive) with the Vision goggles and Grip controller." data-dimension25="$1599">View Deal</a></p></div><h2 class="article-body__section" id="section-best-prime-day-potensic-drone-deals"><span>Best Prime Day Potensic drone deals</span></h2><div class="product"><a data-dimension112="726c4705-540d-43b2-8603-21c3bfb7abfb" data-action="Deal Block" data-label="my review" data-dimension48="my review" data-dimension25="$324" href="https://www.amazon.com/Potensic-Transmission-Vertical-Shooting-QuickShots/dp/B0DKHCZHCY/" 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:78.13%;"><img id="QEqNG4iNnxL2KBjVQKhum8" name="Potensic-Atom-2-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/QEqNG4iNnxL2KBjVQKhum8.jpg" mos="" align="middle" fullscreen="" width="1500" height="1172" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Potensic Atom 2 is <em>even better </em>than its predecessor, as it can capture 48MP stills. In <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-2-review" data-dimension112="726c4705-540d-43b2-8603-21c3bfb7abfb" data-action="Deal Block" data-label="my review" data-dimension48="my review" data-dimension25="$324">my review</a>, I noted that it records stable, smooth 4K/30fps video and features advanced, accurate AI subject tracking, along with a new, easy-to-use controller and app. It’s also lightweight yet sturdy. This deal includes extra batteries, a controller, and a carry bag.<a class="view-deal button" href="https://www.amazon.com/Potensic-Transmission-Vertical-Shooting-QuickShots/dp/B0DKHCZHCY/" target="_blank" rel="nofollow" data-dimension112="726c4705-540d-43b2-8603-21c3bfb7abfb" data-action="Deal Block" data-label="my review" data-dimension48="my review" data-dimension25="$324">View Deal</a></p></div><div class="product"><a data-dimension112="0702164b-46e0-4ba1-ba92-1ab6736bda13" data-action="Deal Block" data-label="The Potensic Atom SE Expansion Kit includes the drone, two batteries, a controller, and a carry bag. The drone weighs less than 249g, so you don't need to register with the F.A.A. in the U.S. The Atom SE is a great starting point for new drone pilots." data-dimension48="The Potensic Atom SE Expansion Kit includes the drone, two batteries, a controller, and a carry bag. The drone weighs less than 249g, so you don't need to register with the F.A.A. in the U.S. The Atom SE is a great starting point for new drone pilots." data-dimension25="$195" href="https://www.amazon.com/gp/aw/d/B0BFWS4N4T/" 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:85.07%;"><img id="MGhcidVaYSkc7Lt5KR98h4" name="Potensic Atom SE deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/MGhcidVaYSkc7Lt5KR98h4.jpg" mos="" align="middle" fullscreen="" width="1500" height="1276" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Potensic Atom SE Expansion Kit includes the drone, two batteries, a controller, and a carry bag. The drone weighs less than 249g, so you don't need to register with the F.A.A. in the U.S. The Atom SE is a great starting point for new drone pilots.<a class="view-deal button" href="https://www.amazon.com/gp/aw/d/B0BFWS4N4T/" target="_blank" rel="nofollow" data-dimension112="0702164b-46e0-4ba1-ba92-1ab6736bda13" data-action="Deal Block" data-label="The Potensic Atom SE Expansion Kit includes the drone, two batteries, a controller, and a carry bag. The drone weighs less than 249g, so you don't need to register with the F.A.A. in the U.S. The Atom SE is a great starting point for new drone pilots." data-dimension48="The Potensic Atom SE Expansion Kit includes the drone, two batteries, a controller, and a carry bag. The drone weighs less than 249g, so you don't need to register with the F.A.A. in the U.S. The Atom SE is a great starting point for new drone pilots." data-dimension25="$195">View Deal</a></p></div><h2 class="article-body__section" id="section-best-prime-day-hoverair-drone-deals"><span>Best Prime Day HoverAir drone deals</span></h2><div class="product"><a data-dimension112="889736dd-5f6d-4b22-9bfb-76e95e739f3a" data-action="Deal Block" data-label="enjoyed using it a lot" data-dimension48="enjoyed using it a lot" data-dimension25="$271" href="https://www.amazon.com/HOVERAir-Self-Flying-Foldable-Intelligent-Hands-Free/dp/B0CQJTHM5B/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1441px;"><p class="vanilla-image-block" style="padding-top:92.23%;"><img id="DNC8WM5nWsq28ydhTF8jj" name="HoverAir-X1-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DNC8WM5nWsq28ydhTF8jj.jpg" mos="" align="middle" fullscreen="" width="1441" height="1329" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Unlike most other drones, you don't need a controller or a smartphone to fly the HoverAir X1, which is why it's a great selfie drone. Simply press the buttons on its body and it will record short-form videos and save them internally. My colleague <a href="https://www.tomsguide.com/features/sorry-dji-this-pocketable-folding-drone-is-the-most-fun-ive-had-flying-in-a-long-time" data-dimension112="889736dd-5f6d-4b22-9bfb-76e95e739f3a" data-action="Deal Block" data-label="enjoyed using it a lot" data-dimension48="enjoyed using it a lot" data-dimension25="$271">enjoyed using it a lot</a> and appreciated its surprisingly good obstacle avoidance and subject tracking features such as follow, rocket, and more.<a class="view-deal button" href="https://www.amazon.com/HOVERAir-Self-Flying-Foldable-Intelligent-Hands-Free/dp/B0CQJTHM5B/" target="_blank" rel="nofollow" data-dimension112="889736dd-5f6d-4b22-9bfb-76e95e739f3a" data-action="Deal Block" data-label="enjoyed using it a lot" data-dimension48="enjoyed using it a lot" data-dimension25="$271">View Deal</a></p></div><div class="product"><a data-dimension112="2647749e-1c2e-4600-9daf-ab817d3a2d68" data-action="Deal Block" data-label="HoverAir X1 ProMax" data-dimension48="HoverAir X1 ProMax" data-dimension25="$594" href="https://www.amazon.com/HOVERAir-Foldable-Automatic-OmniTerrain-SmoothCapture/dp/B0DGTHLVR3/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1409px;"><p class="vanilla-image-block" style="padding-top:95.81%;"><img id="MSdGJ6i6pkFJP3J5ns6pdT" name="HoverAir X1 ProMax deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/MSdGJ6i6pkFJP3J5ns6pdT.jpg" mos="" align="middle" fullscreen="" width="1409" height="1350" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>If you're looking for a step up from the aforementioned HoverAir X1, the <a href="https://www.tomsguide.com/cameras-photography/drones/hoverair-x1-pro-promax-review" data-dimension112="2647749e-1c2e-4600-9daf-ab817d3a2d68" data-action="Deal Block" data-label="HoverAir X1 ProMax" data-dimension48="HoverAir X1 ProMax" data-dimension25="$594">HoverAir X1 ProMax</a> might be right for you. It is the premium model in the X1 lineup and it's capable of capturing 8K/30fps and 4K/60fps video and saving footage to its 64GB internal storage.<a class="view-deal button" href="https://www.amazon.com/HOVERAir-Foldable-Automatic-OmniTerrain-SmoothCapture/dp/B0DGTHLVR3/" target="_blank" rel="nofollow" data-dimension112="2647749e-1c2e-4600-9daf-ab817d3a2d68" data-action="Deal Block" data-label="HoverAir X1 ProMax" data-dimension48="HoverAir X1 ProMax" data-dimension25="$594">View Deal</a></p></div><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-drone-deals"><span>We're tracking all the best Prime Day drone deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Drones&min_discount_ratio=0.95&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }        .tg-df-container .tg-df-carousel-scroll-left,    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-left { left: 8px; }    .tg-df-container .tg-df-carousel-scroll-right { right: 8px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left { left: 0px; }    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right { right: 0px; }    .tg-df-carousel-filters-outer { margin-left: -24px; margin-right: -24px; padding-left: 24px; padding-right: 24px; }    @container tg-df (max-width: 599px) { .tg-df-carousel-filters-outer { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; } }        .tg-df-container .tg-df-carousel-scroll-left:hover,    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-left,    .tg-df-carousel-filters-outer .tg-df-carousel-scroll-right {      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right { right: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-left { left: 0; }    .tg-df-grid-wrapper .tg-df-carousel-scroll-right { right: 0; }        .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-left:hover,    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.parentElement.querySelector('.tg-df-carousel-roundels').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-outer" style="position: relative;">          \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>          \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>        \x3C/div>        \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('.tg-df-carousel-filters-wrap').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3C/div>    \x3C/div>    \x3C/div>      \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cbutton class="tg-df-carousel-scroll-left" type="button" aria-label="Scroll left" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: -200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m15 18-6-6 6-6">\x3C/path>\x3C/svg>\x3C/button>      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>      \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" style="display:none;" onclick="this.parentElement.querySelector('#tg-df-grid').scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }          const website = params.get('website') || 'tomsguide';          this.website = website;          if (website === 'techradar') {            const squadHeader = this.root.querySelector('.tg-df-savings-squad-header');            if (squadHeader) {               const pic = squadHeader.querySelector('picture');               if (pic) pic.style.display = 'none';            }            const style = document.createElement('style');            style.innerHTML = `              .tg-df-container .hawk-affiliate-link-deal-button { background-color: #5DAF08 !important; }              .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a8c06 !important; }            `;            this.root.appendChild(style);          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }          this.setupScrollListeners();        }        setupScrollListeners() {          const containers = [             this.root.querySelector('.tg-df-carousel-roundels'),             this.root.querySelector('.tg-df-carousel-filters-wrap'),             this.root.querySelector('#tg-df-grid')          ];                    containers.forEach(container => {             if (!container) return;                          const checkScroll = () => {                if (!container.parentElement) return;                const leftBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-left');                const rightBtn = container.parentElement.querySelector('.tg-df-carousel-scroll-right');                                if (leftBtn) {                   if (container.scrollLeft <= 5) leftBtn.style.display = 'none';                   else leftBtn.style.display = 'flex';                }                                if (rightBtn) {                   if (container.scrollWidth <= container.clientWidth) {                       rightBtn.style.display = 'none';                   } else if (container.scrollLeft >= container.scrollWidth - container.clientWidth - 5) {                       rightBtn.style.display = 'none';                   } else {                       rightBtn.style.display = 'flex';                   }                }             };                          container.addEventListener('scroll', checkScroll);             checkScroll();                          window.addEventListener('resize', checkScroll);                          const observer = new MutationObserver(checkScroll);             observer.observe(container, { childList: true, subtree: true, characterData: false });          });        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = (this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;          } else {             this.displayLimit += (this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const encodedTag = encodeURIComponent(this.activeDealTag.toLowerCase().replace(/\s+/g, '-'));             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    if (this.rowsSelect && this.rowsSelect.value) {            url.searchParams.append('rows', this.rowsSelect.value);          } else {             url.searchParams.append('rows', '12'); // default          }          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = 12;          if (this.rowsSelect && this.rowsSelect.value) {            rowLimit = parseInt(this.rowsSelect.value, 10) || 12;          }          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map((v, idx) => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              const revenueIdVal = generateRevenueId(linkUrl, offerName, merchantName, null);              const rewrittenLinkUrl = rewriteAffiliateLink(linkUrl, area, revenueIdVal);              return `                \x3Ca href="${this.escapeHTML(rewrittenLinkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item"                  data-action="voucher-click"                  data-product-name="${this.escapeHTML(offerName)}"                  data-merchant-name="${this.escapeHTML(merchantName)}"                  data-analytics-id="${this.escapeHTML(offerObj.offer_id || offerObj.id || v.id || '')}"                  data-price=""                  data-previous-price=""                  data-original-link="${this.escapeHTML(linkUrl)}"                  data-revenue-id="${revenueIdVal}"                  data-index="${idx}"                  data-total="${offers.length}"                  data-in-stock="true"                  data-currency="USD"                  data-model-id="${this.escapeHTML(offerObj.model_id || v.model_id || offerObj.id || v.id || '')}"                  data-merchant-id="${this.escapeHTML(offerObj.merchant_id || offerObj.merchant?.id || '')}"                >                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });            // Attach voucher click tracking            const voucherBtns = content.querySelectorAll('[data-action="voucher-click"]');            voucherBtns.forEach(btn => {              btn.addEventListener('click', (e) => {                if (e.target.closest('[data-action="copy-code"]')) return;                                const productName = btn.getAttribute('data-product-name');                const merchantNameAttr = btn.getAttribute('data-merchant-name');                const productId = btn.getAttribute('data-analytics-id');                const price = parseFloat(btn.getAttribute('data-price')) || null;                const prevPriceStr = btn.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = btn.getAttribute('data-original-link');                const rewrittenLink = btn.getAttribute('href');                const revenueId = btn.getAttribute('data-revenue-id');                const index = parseInt(btn.getAttribute('data-index'), 10) || 0;                const inStock = btn.getAttribute('data-in-stock') === 'true';                const totalText = btn.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: btn.getAttribute('data-model-id') || null,                    matchId: btn.getAttribute('data-match-id') || null,                    brand: btn.getAttribute('data-model-brand') || null,                    parent: btn.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: btn.getAttribute('data-merchant-id') || null,                    network: btn.getAttribute('data-merchant-network') || null,                    url: btn.getAttribute('data-merchant-url') || null,                    name: merchantNameAttr                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: btn.getAttribute('data-currency') || 'USD'                };                if (typeof trackDealClick === 'function') {                  trackDealClick(trackingParams);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = deal.merchant && deal.merchant.toLowerCase().includes('amazon') ? '' : `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;          // Inject JSON-LD          try {            let targetNode = this.hostContainer || document.head;            let jsonLdScript = targetNode.querySelector('#tg-df-json-ld-' + this.widgetId);            if (!jsonLdScript) {                jsonLdScript = document.createElement('script');                jsonLdScript.type = 'application/ld+json';                jsonLdScript.id = 'tg-df-json-ld-' + this.widgetId;                targetNode.appendChild(jsonLdScript);            }            const jsonLdData = {              "@context": "https://schema.org",              "@type": "ItemList",              "itemListElement": displayDeals.slice(0, this.displayLimit).map((deal, index) => {                 let isoCurrency = "USD";                 if (deal.currency === '£') isoCurrency = "GBP";                 else if (deal.currency === '€') isoCurrency = "EUR";                 else if (deal.currency === 'A$') isoCurrency = "AUD";                 else if (deal.currency === 'CA$') isoCurrency = "CAD";                 const areaCode = typeof this.getAreaCode === 'function' ? this.getAreaCode() : 'US';                 const revenueId = typeof generateRevenueId === 'function' ? generateRevenueId(deal.url, deal.title, deal.merchant, null) : '';                 const rewrittenLink = typeof rewriteAffiliateLink === 'function' ? rewriteAffiliateLink(deal.url, areaCode, revenueId) : deal.url;                 return {                   "@type": "ListItem",                   "position": index + 1,                   "item": {                     "@type": "Product",                     "name": deal.title,                     "image": deal.image || "",                     "description": deal.description || "",                     "brand": {                       "@type": "Brand",                       "name": deal.brand || ""                     },                     "offers": {                       "@type": "Offer",                       "priceCurrency": isoCurrency,                       "price": deal.rawPrice || 0,                       "url": rewrittenLink,                       "seller": {                         "@type": "Organization",                         "name": deal.merchant || ""                       }                     }                   }                 };              }).filter(item => item.item.name)            };            jsonLdScript.textContent = JSON.stringify(jsonLdData);          } catch(e) { console.warn("JSON-LD generation failed", e); }                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let rightChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             let leftChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-left');             if (this.getViewMode() === 'carousel') {                 // The observer set up in setupScrollListeners handles visibility.                 if (rightChevron) rightChevron.style.display = 'flex';                 if (leftChevron) leftChevron.style.display = 'none'; // reset correctly             } else {                 if (rightChevron) rightChevron.style.display = 'none';                 if (leftChevron) leftChevron.style.display = 'none';             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12);                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');            if (!this.processArticleFired) {                  this.processArticleFired = true;                  document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));               }            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ No, you can't buy the DJI Osmo Pocket 4 in the U.S. — but you can get the Osmo Pocket 3 for $90 off right now ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-3-amazon-prime-day-2026</link>
                                                                            <description>
                            <![CDATA[ The DJI Osmo Pocket 4 is a fantastic gimbal camera which is why it's a shame that it isn't available in the U.S. But its predecessor is available and it's $90 off. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">zYVMbj3QzS8XduUR9JeUam</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/29MaZDuoprcuAi8fcFdnd-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 22 Jun 2026 17:34:15 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/29MaZDuoprcuAi8fcFdnd-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A DJI Osmo Pocket 3]]></media:description>                                                            <media:text><![CDATA[A DJI Osmo Pocket 3]]></media:text>
                                <media:title type="plain"><![CDATA[A DJI Osmo Pocket 3]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/29MaZDuoprcuAi8fcFdnd-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I love going to concerts and music festivals, and I've always been a little sad that many venues don't allow cameras with changeable lenses — which is understandable, of course. Thankfully, there are ways around it, and you don't always have to rely on your smartphone for getting beautiful footage of your favorite artists.</p><p>The solution? Why, a gimbal camera, of course! We test many of these nifty devices, and unfortunately, the best we've tested so far isn't available in the U.S. The <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review">DJI Osmo Pocket 4</a> is a 5-star gimbal camera that doesn't give you much to complain about. Well, except for the fact that it can't be bought in the States.</p><p>You know what can be bought in the U.S., though? Its predecessor: the 4.5-star <a href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0CG19FGQ5/" target="_blank" rel="nofollow">DJI Osmo Pocket 3 which is currently $90 off at Amazon</a>. The <a href="https://www.tomsguide.com/reviews/dji-osmo-pocket-3">Osmo Pocket 3</a>, even three years on, is one of the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras</a> you can buy, and it's perfect for travel, vlogging, concerts, festivals, even professional videography.</p><div class="product"><a data-dimension112="98b0522b-1132-4e70-99f4-2b1871e8ceb7" data-action="Deal Block" data-label="The DJI Osmo Pocket 3 is the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage." data-dimension48="The DJI Osmo Pocket 3 is the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage." data-dimension25="$539" href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0CG19FGQ5/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:560px;"><p class="vanilla-image-block" style="padding-top:103.21%;"><img id="k2TTAhmN9A6tZiDzvApqCP" name="Summer-festival-gadgets-deal-box-3" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/k2TTAhmN9A6tZiDzvApqCP.png" mos="" align="middle" fullscreen="" width="560" height="578" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The DJI Osmo Pocket 3 is the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage.<a class="view-deal button" href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0CG19FGQ5/" target="_blank" rel="nofollow" data-dimension112="98b0522b-1132-4e70-99f4-2b1871e8ceb7" data-action="Deal Block" data-label="The DJI Osmo Pocket 3 is the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage." data-dimension48="The DJI Osmo Pocket 3 is the perfect tool for vloggers and content creators, providing an affordable gimbal solution to give their video a professional look. It features awesome stabilization, strong low light performance and great battery life, all the while capturing stunning 4K footage." data-dimension25="$539">View Deal</a></p></div><p>There's a lot to love about the Osmo Pocket 3. It features a 1-inch CMOS sensor that's great for capturing plenty of detail, regardless of the lighting conditions. That's right: its low-light performance is formidable. It's an all-in-one camera which is equipped with a built-in three-axis gimbal that enables you to capture super smooth video effortlessly.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/d6cpvUXrrxs" allowfullscreen></iframe></div></div><p>The Osmo Pocket 3 is fitted with a 2-inch touchscreen which lets you quickly change resolution and frame rate, as well as focus modes and other shooting settings. It also pivots 90° so you can switch between horizontal and vertical shooting as needed. One of the Osmo Pocket 3's biggest selling points is its design: it measures and weighs barely anything, so you can slip it into your pocket, akin to a phone, and be on your way.</p><p>Autofocus performance is outstanding, as the Osmo Pocket 3 effectively and accurately tracks humans as well as inanimate objects. Stabilization is impeccable thanks to the three-axis gimbal, which I briefly mentioned earlier. 4K/60fps video is gorgeous, with accurate color reproduction. And in our testing, the Osmo Pocket 3 lasted nearly two hours between charges.</p><p>This <a href="https://www.amazon.com/DJI-Vlogging-Stabilization-Tracking-Photography/dp/B0CG19FGQ5/" target="_blank" rel="nofollow">early Amazon Prime Day deal cuts the price of the Osmo Pocket 3 Creator Combo by 14%</a>, so you can save $90 on it. The Creator Combo includes a wide-angle lens, DJI Mic 2 transmitter and receiver, a mini tripod, and a carry case for the camera and all its accessories. Seriously, it's the best camera to get in lieu of the Osmo Pocket 4.</p><h3 class="article-body__section" id="section-we-re-tracking-all-the-best-prime-day-home-appliance-deals"><span>We're tracking all the best Prime Day home appliance deals</span></h3><div class="vizualizer-embed"><div class="tg-df-widget-host" data-widget-config="?search=Home+Appliances&price=50_&min_discount_ratio=0.95&offer_type=all&rows=4&widget_title=Top+Deals+Handpicked+by+Our+Editors&widget_subtitle=Discover+the+best+discounts+currently+available%2C+curated+daily+by+the+Tom%27s+Guide+Savings+Squad.&show_countdown=true&bg_color=transparent" data-vizualizer-embed="true"></div>    <script>    /**     * Tom's Guide Deals Finder - Vanilla JS Encapsulated Engine     */    (function() {      // --- Freyr Analytics Adapter ---      function initAnalytics() {        window.dataLayer = window.dataLayer || [];        window.googletag = window.googletag || {};        window.googletag.cmd = window.googletag.cmd || [];        window.hawk = window.hawk || { analytics: { freyr: [] } };        window.hawk.analytics = window.hawk.analytics || { freyr: [] };        window.hawk.analytics.freyr = window.hawk.analytics.freyr || [];        window.freyr = window.freyr || { cmd: [] };        const scriptSrc = 'https://freyr.futurecdn.net/freyr.js';        const hostname = typeof window !== 'undefined' ? window.location.hostname : '';        const isTestEnv = typeof window.navigator !== 'undefined' && (window.navigator.webdriver || window.navigator.userAgent.includes('Headless'));        const shouldSendRealAnalytics = !isTestEnv && hostname && hostname !== 'localhost' && hostname !== '127.0.0.1' && !hostname.includes('run.app');        if (shouldSendRealAnalytics && !document.querySelector(`script[src="${scriptSrc}"]`)) {          const script = document.createElement('script');          script.src = scriptSrc;          script.async = true;          document.head.appendChild(script);        }      }      function storeEventForDebug(name, data) {        if (!window.hawk || !window.hawk.analytics || !window.hawk.analytics.freyr) return;        window.hawk.analytics.freyr.push({ name, data });        try {          if (typeof window !== 'undefined' && window.localStorage) {            window.localStorage.setItem("hawk", JSON.stringify(window.hawk));          }        } catch (e) {          // Ignore storage issues        }        try {          window.dispatchEvent(new CustomEvent("hawk-analytics-update"));        } catch (e) {}      }      function sendToFreyr(eventName, data) {        if (typeof window === 'undefined') return;        window.freyr = window.freyr || { cmd: [] };        window.freyr.cmd.push(() => {          if (window.freyr && window.freyr.pushAndUpdate) {            window.freyr.pushAndUpdate(eventName, data);          }        });      }      function sendEvent(event, skip = false) {        try {          storeEventForDebug(event.name, event.data);          if (!skip) {            sendToFreyr(event.name, event.data);          }        } catch (e) {          // Ensure tracking errors don't surface to the user        }      }      function getCookie(name) {        try {          const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));          return match ? match[2] : null;        } catch (e) {          return null;        }      }      function normalizeCurrency(symbol) {        const map = {          '£': 'GBP',          '$': 'USD',          'A$': 'AUD',          'CA$': 'CAD',          '€': 'EUR'        };        return map[symbol] || symbol;      }      function trackElementInteraction(props) {        sendEvent({          name: 'elementInteraction',          data: {            element: {              action: props.action || "click",              id: props.id || undefined,              class: props.class || undefined,              name: props.name || undefined,              text: props.text || undefined,              label: props.label || undefined,              container: props.container || undefined,              url: props.url || undefined,              articleId: props.articleId || undefined            }          }        });      }      function generateRevenueId(url, productName, merchantName, modelId) {        const str = `${window.location.href}|${productName}|${merchantName}|${modelId || ''}|${new Date().toDateString()}|tomsguide`;        let hash = 0;        for (let i = 0; i < str.length; i++) {          const char = str.charCodeAt(i);          hash = ((hash << 5) - hash) + char;          hash = hash & hash;        }        let numericStr = Math.abs(hash).toString();        while (numericStr.length < 19) {          numericStr += Math.floor(Math.random() * 10).toString();        }        return numericStr.substring(0, 19);      }      function rewriteAffiliateLink(url, territory, revenueId) {        if (!url) return url;        const t = (territory || 'gb').toLowerCase();        return url.replace(/hawk-custom-tracking/g, `tomsguide-${t}-${revenueId}`);      }      function trackHawkEvent(params) {        const { clickType, widgetId, productCategoryName, product, productsArray, zeroBasedProductIndexOrNull, totalDealsOrProducts, areaClicked, merchant, revenueId, isoCurrencyCode, queryName, widgetTypeName } = params;        const data = {          event: "hawkEvent",          category: "Affiliates",          affiliate: {            action: {              type: clickType,              id: widgetId,              event: clickType === "appeared" ? "viewed" : "Click from",              timestamp: Date.now()            },            component: {              flag: "Editor",              product: productCategoryName || "deals",              category: `Signal Deal Finder ${widgetTypeName || "Carousel"} widget`,              type: clickType === "appeared" ? "review" : "signal product",              label: queryName || (product ? (product.name || "") : ""),              index: zeroBasedProductIndexOrNull === null || zeroBasedProductIndexOrNull === undefined ? -1 : zeroBasedProductIndexOrNull,              linkCount: totalDealsOrProducts || 0,              blockLayout: "",              areaClicked: areaClicked || ""            }          },          products: productsArray || (product && merchant ? [            {              product: {                primary: {                  id: product.id || product.matchId || null,                  name: product.name,                  type: "deal",                  price: product.price,                  previousPrice: product.previousPrice || null,                  currency: isoCurrencyCode || "USD",                  preorder: false,                  labels: [],                  link: product.link,                  originalLink: product.originalLink || null,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: null,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: product.globalId || null,                  inStock: product.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: isoCurrencyCode || "USD"                }              },              merchant: {                id: merchant.id || null,                name: merchant.name,                url: merchant.url || null,                network: merchant.network || null              },              model: {                id: product.modelId || null,                brand: product.brand || null,                name: product.name,                parent: product.parent || null              }            }          ] : []),          reviews: [],          _clear: true,          "gtm.uniqueEventId": Date.now() % 10000        };        sendEvent({ name: 'hawkEvent', data });      }      function trackDealClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card" });      }      function trackViewSimilarClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Product Card View Similar" });      }      function trackPriceComparisonClick(params) {        trackHawkEvent({ ...params, clickType: "retailer", areaClicked: "Signal Price Comparison" });      }      function trackReviewClick(params) {        trackHawkEvent({ ...params, clickType: "review", areaClicked: "Signal Product Card Review Link" });      }      function trackShare(params) {        trackHawkEvent({ ...params, clickType: "share", areaClicked: "Signal Product Card Share" });      }      function trackDealsAppeared(widgetId, deals, revenueId, currency, queryName, widgetTypeName) {         if (!deals || deals.length === 0) return;                  const productsArray = deals.slice(0, 50).map((deal) => {            let voucherPct = null;            let rawPrice = parseFloat(deal.rawPrice) || parseFloat(deal.price) || null;            let rawMsrp = parseFloat(deal.rawMsrp) || parseFloat(deal.msrp) || null;            if (rawMsrp > rawPrice && rawPrice > 0) {              voucherPct = Math.round((1 - (rawPrice / rawMsrp)) * 100);            }            let numId = null;            if (deal.externalProductId && !isNaN(parseInt(deal.externalProductId))) {              numId = parseInt(deal.externalProductId);            } else if (deal.id && !isNaN(parseInt(deal.id))) {              numId = parseInt(deal.id);            } else {              numId = deal.matchId || null;            }            return {              product: {                primary: {                  id: numId,                  name: deal.productName || deal.title || "",                  type: "deal",                  price: rawPrice,                  previousPrice: rawMsrp,                  currency: currency || 'USD',                  preorder: false,                  labels: deal.modelBrand || deal.brand ? [                     { type: "brand", value: deal.modelBrand || deal.brand }                  ] : [],                  link: deal.url,                  originalLink: deal.url,                  revenueId: revenueId || null,                  startTime: null,                  endTime: null,                  voucherCode: null,                  voucherAudience: null,                  voucherPercentageSaving: voucherPct,                  voucherMoneySaving: null,                  voucherType: null,                  offerExclusive: false,                  offerScope: null,                  globalId: deal.productKey || null,                  inStock: deal.inStock !== false,                  contractProvider: null,                  contractMinutes: null,                  contractTexts: null,                  contractData: null,                  contractLength: null,                  contractMonthlyPrice: null,                  contractCurrency: currency || 'USD'                }              },              merchant: {                id: deal.merchantId ? parseInt(deal.merchantId) : null,                name: deal.merchant || "Retailer",                url: deal.merchantUrl || null,                network: deal.merchantNetwork || null              },              model: {                id: deal.modelId ? parseInt(deal.modelId) : null,                brand: deal.modelBrand || deal.brand || null,                name: deal.productName || deal.title || "",                parent: deal.modelParent || null              }            };         });                  trackHawkEvent({             clickType: "appeared",             widgetId: widgetId,             productCategoryName: "deals",             zeroBasedProductIndexOrNull: null,             totalDealsOrProducts: deals.length,             productsArray: productsArray,             queryName: queryName,             widgetTypeName: widgetTypeName         });      }      // 1. Setup Shadow DOM Sandbox      const currentScript = document.currentScript;      let hostContainer = null;      let template = null;            if (currentScript) {        let prev = currentScript.previousElementSibling;        while (prev) {          if (prev.tagName === 'TEMPLATE' && prev.classList.contains('tg-df-widget-template')) {            template = prev;          } else if (prev.tagName === 'DIV' && prev.classList.contains('tg-df-widget-host') && !prev.hasAttribute('data-initialized')) {            hostContainer = prev;            break;          }          prev = prev.previousElementSibling;        }      }            // Fallbacks in case script is deferred      if (!hostContainer) {        const hosts = document.querySelectorAll('.tg-df-widget-host:not([data-initialized])');        if (hosts.length > 0) hostContainer = hosts[0];      }            // Safely embedded template for CMS environments      const rawTemplate = `  \x3Cstyle>    /* --- Shadow DOM Base Reset --- */    *, *::before, *::after {      box-sizing: border-box;    }    img, picture, svg, video {      max-width: 100%;      height: auto;      display: block;    }    /*       1. Scoped CSS for Tom's Guide Deals Widget       All classes are prefixed with \`tg-df-\` to prevent CMS style leakage.    */    .tg-df-container {      container-type: inline-size;      container-name: tg-df;      --tg-df-blue: #1F69FF;      --tg-df-blue-hover: #004d8c;      --tg-df-text: #222222;      --tg-df-text-muted: #555555;      --tg-df-bg: #ffffff;      --tg-df-bg-secondary: #f4f4f4;      --tg-df-border: #e2e8f0;      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;      color: var(--tg-df-text);      background-color: transparent;       width: 100%;      max-width: 1200px;      margin: 0 auto;      padding-bottom: 24px;    }    .tg-df-container *, .tg-df-container *::before, .tg-df-container *::after {      margin: 0;      padding: 0;      box-sizing: border-box;    }    .tg-df-container img {      border: none;      margin: 0;      padding: 0;    }    .tg-df-container a {      text-decoration: none;      color: inherit;    }    /*       2. Search & Filter Bar    */    .tg-df-controls {      display: flex;      flex-direction: column;      align-items: center;      gap: 20px;      margin-bottom: 32px;      width: 100%;    }    .tg-df-top-bar {      display: flex;      width: 100%;      max-width: 760px;      gap: 12px;      align-items: center;    }    .tg-df-search-wrapper {      position: relative;      flex: 1;      width: 100%;      box-shadow: 0 8px 24px rgba(0,0,0,0.06);      border-radius: 40px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      z-index: 100;    }    .tg-df-autocomplete-dropdown {      position: absolute;      top: calc(100% + 4px);      left: 0;      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      max-height: 300px;      overflow-y: auto;      z-index: 200;      display: none;    }    .tg-df-autocomplete-dropdown.active {      display: block;    }    .tg-df-autocomplete-item {      padding: 12px 24px;      cursor: pointer;      font-size: 14px;      color: var(--tg-df-text);      transition: background 0.1s ease;    }    .tg-df-autocomplete-item:hover {      background: var(--tg-df-bg-secondary);    }    .tg-df-search-input {      width: 100%;      padding: 16px 64px 16px 24px;      font-size: 16px;      border: 2px solid transparent;      border-radius: 40px;      outline: none;      transition: border-color 0.2s ease, box-shadow 0.2s ease;      color: var(--tg-df-text);      background: transparent;    }    .tg-df-search-input:focus {      border-color: transparent;      box-shadow: 0 0 0 3px rgba(0, 108, 196, 0.15);    }    .tg-df-search-input::placeholder {      color: #999999;    }        .tg-df-search-btn {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      width: 40px;      height: 40px;      border-radius: 50%;      background: #222;      border: none;      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: background 0.2s ease;    }        .tg-df-search-btn:hover {      background: #000;    }    .tg-df-search-icon {      width: 16px;      height: 16px;      fill: #fff;    }    .tg-df-settings-wrapper {      position: relative;    }        .tg-df-settings-btn {      width: 48px;      height: 48px;      border-radius: 50%;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      box-shadow: 0 4px 12px rgba(0,0,0,0.04);      display: flex;      align-items: center;      justify-content: center;      cursor: pointer;      transition: all 0.2s ease;      color: var(--tg-df-text-muted);      flex-shrink: 0;    }    .tg-df-settings-btn:hover {      background: var(--tg-df-bg-secondary);      border-color: #0000ff;      color: var(--tg-df-text);    }    .tg-df-settings-btn svg {      width: 24px;      height: 24px;      fill: currentColor;    }    .tg-df-settings-dropdown {      position: absolute;      top: calc(100% + 8px);      right: 0;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 12px;      box-shadow: 0 8px 32px rgba(0,0,0,0.12);      width: 280px;      padding: 20px;      display: none;      z-index: 100;      flex-direction: column;      gap: 20px;    }    .tg-df-settings-dropdown.active {      display: flex;    }        .tg-df-settings-dropdown-backdrop {      display: none;      position: fixed;      inset: 0;      z-index: 99;    }        .tg-df-settings-dropdown-backdrop.active {      display: block;    }    .tg-df-setting-item {      display: flex;      flex-direction: column;      gap: 10px;    }    .tg-df-setting-label {      font-size: 11px;      font-weight: 700;      color: var(--tg-df-text-muted);      text-transform: uppercase;      letter-spacing: 0.5px;    }        .tg-df-region-select {        padding: 10px 12px;        border-radius: 8px;        border: 1px solid var(--tg-df-border);        font-size: 15px;        outline: none;        background: var(--tg-df-bg-secondary);        color: var(--tg-df-text);        cursor: pointer;        width: 100%;    }    .tg-df-toggle {        position: relative;        display: inline-block;        width: 44px;        height: 24px;        flex-shrink: 0;    }    .tg-df-toggle input {        opacity: 0;        width: 0;        height: 0;    }    .tg-df-slider {        position: absolute;        cursor: pointer;        top: 0; left: 0; right: 0; bottom: 0;        background-color: #ccc;        transition: .2s;        border-radius: 24px;    }    .tg-df-slider:before {        position: absolute;        content: "";        height: 18px;        width: 18px;        left: 3px;        bottom: 3px;        background-color: white;        transition: .2s;        border-radius: 50%;    }    .tg-df-toggle input:checked + .tg-df-slider {        background-color: #1F69FF;    }    .tg-df-toggle input:checked + .tg-df-slider:before {        transform: translateX(20px);    }    .tg-df-dl-row {        flex-direction: row;        align-items: center;        justify-content: space-between;    }    .tg-df-dl-row-text {        font-size: 14px;        font-weight: 600;        color: var(--tg-df-text);    }    .tg-df-dl-row-subtext {        font-size: 12px;        font-weight: 400;        line-height: 1.3;        color: var(--tg-df-text-muted);        margin-top: 4px;        display: block;    }    .tg-df-filters {      display: flex;      gap: 12px;      justify-content: center;      flex-wrap: wrap;    }    .tg-df-sort-wrapper {      position: relative;      display: flex;      align-items: center;    }        .tg-df-sort-icon {      position: absolute;      left: 14px;      width: 14px;      height: 14px;      fill: var(--tg-df-text-muted);      pointer-events: none;    }    .tg-df-sort-select, .tg-df-filter-select {      padding: 10px 36px 10px 38px;      font-size: 14px;      border: 1px solid var(--tg-df-border);      border-radius: 100px;      outline: none;      appearance: none;      background-color: var(--tg-df-bg-secondary);      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12'%3E%3Cpath fill='%23555555' d='M6 8L1 3h10z'/%3E%3C/svg%3E");      background-repeat: no-repeat;      background-position: right 14px center;      color: var(--tg-df-text);      cursor: pointer;      font-weight: 500;      transition: all 0.2s ease;    }        .tg-df-price-input::-webkit-outer-spin-button,    .tg-df-price-input::-webkit-inner-spin-button {      -webkit-appearance: none;      margin: 0;    }    .tg-df-price-input {      -moz-appearance: textfield;    }    .tg-df-sort-select:hover, .tg-df-filter-select:hover {      background-color: #e2e8f0;    }    .tg-df-multiselect-container {      position: relative;    }        .tg-df-multiselect-trigger {      display: block;      background: #fff;      user-select: none;      width: 100%;      overflow: hidden;      white-space: nowrap;      text-overflow: ellipsis;    }        .tg-df-multiselect-dropdown {      display: none;      position: absolute;      top: calc(100% + 4px);      left: 0;      width: 100%;      min-width: 220px;      max-height: 300px;      overflow-y: auto;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);      z-index: 100;      padding: 8px 0;    }    .tg-df-multiselect-dropdown.active {      display: block;    }    .tg-df-ms-option {      padding: 8px 16px;      display: flex;      align-items: center;      gap: 8px;      cursor: pointer;      font-size: 14px;    }    .tg-df-ms-option:hover {      background-color: var(--tg-df-bg-secondary);    }        .tg-df-ms-option input {      cursor: pointer;      accent-color: #1f69ff;    }    .tg-df-sort-select:focus, .tg-df-filter-select:focus {      border-color: #0000ff;      box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.2);      background-color: var(--tg-df-bg);    }    /*       3. Deal Grid Layout    */    .tg-df-grid.tg-df-grid-auto {      padding-top: 24px;    }    .tg-df-grid, .tg-df-grid.layout-grid {      display: grid;      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));      gap: 10px;    }    .tg-df-grid.layout-row {      grid-template-columns: 1fr;      gap: 16px;    }        .tg-df-grid.layout-row .tg-df-card {      flex-direction: row;      align-items: stretch;      height: auto;      box-shadow: none;      border-bottom: 1px solid var(--tg-df-border);    }    .tg-df-grid.layout-row .tg-df-card:hover {      box-shadow: none;    }    .tg-df-grid.layout-row .tg-df-card-image-box {      width: 140px;      min-width: 140px;      aspect-ratio: 3/4;      border-right: none;      padding: 16px 16px 16px 32px;    }    .tg-df-grid.layout-row .tg-df-card-body {      padding: 16px;      justify-content: space-between;    }    .tg-df-grid.layout-row .tg-df-card-title {      font-size: 15px;      margin-bottom: 16px;    }    .tg-df-grid.layout-row .tg-df-card-stars { margin-bottom: 8px; }    .tg-df-grid.layout-row .tg-df-card-footer {      flex-direction: column;      align-items: flex-start;      gap: 0;    }    .tg-df-grid.layout-row .tg-df-card-merchant-pill {      margin-bottom: 4px;    }    .tg-df-grid.layout-row .tg-df-card-price-group {      margin-bottom: 8px;    }    .tg-df-grid.layout-row .tg-df-price-group {      width: auto;    }    .tg-df-grid.layout-row .tg-df-card-cta {      width: 100%;      max-width: 200px;      padding: 10px 24px;      font-size: 13px;      flex-shrink: 0;      text-align: center;      justify-content: center;    }    /*       4. Deal Card Design    */    .tg-df-card {      position: relative;      display: flex;      flex-direction: column;      background-color: #ffffff;      border-radius: 0;      overflow: hidden;      transition: transform 0.2s ease, box-shadow 0.2s ease;      text-decoration: none;      color: inherit;      height: 100%;      box-shadow: 0 0 16px rgba(0, 0, 0, 0.08);      border: 1px solid var(--tg-df-border);    }    .tg-df-card:hover {      box-shadow: 0 0 24px rgba(0, 0, 0, 0.12);    }    .tg-df-card-image-box {      width: 100%;      aspect-ratio: 3/4;      background-color: #f8f8f8;      display: flex;      align-items: center;      justify-content: center;      position: relative;      overflow: hidden;      padding: 32px;      flex: 0 0 auto;    }    .tg-df-card-image {      max-width: 100%;      max-height: 100%;      width: auto;      height: auto;      object-fit: contain;      mix-blend-mode: multiply; /* Helps white background images blend into secondary bg */      transition: transform 0.3s ease;    }    .tg-df-card:hover .tg-df-card-image {      transform: scale(1.05); /* Zoom in on hover */    }    .tg-df-card-discount-badge {      position: absolute;      top: 12px;      left: 12px;      background: #dc2626; /* Red */      color: #ffffff;      padding: 6px 8px;      font-size: 11px;      font-weight: 500;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      z-index: 10;    }        .tg-df-card-merchant-pill {      display: block;      padding: 0;      font-size: 11px;      font-weight: 600;      text-transform: uppercase;      letter-spacing: 0.5px;      border-radius: 0;      color: var(--tg-df-text-muted);      margin-bottom: 8px;      white-space: nowrap;      overflow: hidden;      text-overflow: ellipsis;    }    .tg-df-card-body {      padding: 16px;      display: flex;      flex-direction: column;      flex-grow: 1;      min-width: 0;    }    .tg-df-card-badges {      display: flex;      flex-wrap: wrap;      gap: 6px;      margin-bottom: 8px;    }    .tg-df-tag {      display: inline-flex;      align-items: center;      padding: 4px 6px;      font-size: 11px;      font-weight: 700;      text-transform: uppercase;      border-radius: 4px;      gap: 4px;    }    .tg-df-tag-prime {      background-color: #00A8E1;      color: #fff;    }    .tg-df-tag-coupons {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-coupons:hover {      background-color: #e2e8f0;    }        .tg-df-tag-outline {      background-color: #f1f5f9;      color: #334155;      border: 1px solid #cbd5e1;      cursor: pointer;      transition: background-color 0.2s;    }    .tg-df-tag-outline:hover {      background-color: #e2e8f0;    }        @keyframes tg-df-spin {      0% { transform: rotate(0deg); }      100% { transform: rotate(360deg); }    }    .tg-df-coupon-spinner {      border: 2px solid #e2e8f0;      border-top: 2px solid #3b82f6;      border-radius: 50%;      width: 14px;      height: 14px;      animation: tg-df-spin 1s linear infinite;      margin: 4px 8px;      display: inline-block;    }        /* Vouchers Modal */    .tg-df-modal-backdrop {      position: fixed;      top: 0; left: 0; right: 0; bottom: 0;      background: rgba(0,0,0,0.5);      z-index: 10000;      display: flex;      align-items: center;      justify-content: center;      opacity: 0;      pointer-events: none;      transition: opacity 0.3s;    }    .tg-df-modal-backdrop.active {      opacity: 1;      pointer-events: auto;    }    .tg-df-modal {      background: #fff;      border-radius: 12px;      width: 90%;      max-width: 400px;      max-height: 80vh;      display: flex;      flex-direction: column;      box-shadow: 0 10px 40px rgba(0,0,0,0.2);      transform: translateY(20px);      transition: transform 0.3s;    }    .tg-df-modal-backdrop.active .tg-df-modal {      transform: translateY(0);    }    .tg-df-modal-header {      padding: 16px;      border-bottom: 1px solid #e2e8f0;      display: flex;      align-items: center;      justify-content: space-between;    }    .tg-df-modal-title {      font-size: 16px;      font-weight: 600;      margin: 0;    }    .tg-df-modal-close {      background: none;      border: none;      cursor: pointer;      padding: 4px;      color: #64748b;    }    .tg-df-modal-body {      padding: 16px;      overflow-y: auto;    }    .tg-df-voucher-item {      padding: 12px;      border: 1px dashed #cbd5e1;      border-radius: 8px;      margin-bottom: 10px;      background: #f8fafc;      display: flex;      align-items: center;      gap: 12px;      text-decoration: none;      color: inherit;      transition: background-color 0.2s, border-color 0.2s;    }    .tg-df-voucher-item:hover {      background: #f1f5f9;      border-color: #94a3b8;    }    .tg-df-voucher-item:last-child {      margin-bottom: 0;    }    .tg-df-voucher-logo {      width: 48px;      height: 48px;      object-fit: contain;      border-radius: 4px;      background: #fff;      border: 1px solid #e2e8f0;      flex-shrink: 0;    }    .tg-df-voucher-content {      flex: 1;      min-width: 0;    }    .tg-df-voucher-title {      font-size: 14px;      font-weight: 600;      margin: 0 0 4px 0;      line-height: 1.3;      color: #0f172a;    }    .tg-df-voucher-expiry {      font-size: 12px;      color: #64748b;      display: flex;      align-items: center;      gap: 4px;      margin-top: 6px;    }    .tg-df-voucher-code {      display: inline-flex;      align-items: center;      background: #f1f5f9;      border: 1px dashed #cbd5e1;      padding: 6px 10px;      font-family: monospace;      font-weight: 700;      font-size: 14px;      color: #0f172a;      border-radius: 4px;      margin-top: 8px;      cursor: pointer;      transition: all 0.2s ease;    }    .tg-df-voucher-code:hover {      background: #e2e8f0;      border-color: #94a3b8;    }    .tg-df-voucher-code.copied {      background: #ecfdf5;      border-color: #10b981;      color: #10b981;    }    .tg-df-voucher-cta {      display: inline-block;      margin-top: 8px;      font-size: 13px;      font-weight: 600;      color: #2563eb;      text-decoration: none;    }    .tg-df-card-title {      font-size: 15px;      font-weight: 400;      line-height: 1.4;      margin: 0 0 12px 0;      color: var(--tg-df-text);      display: -webkit-box;      -webkit-line-clamp: 2;      -webkit-box-orient: vertical;      overflow: hidden;    }    .tg-df-card-footer {      margin-top: auto;      display: flex;      flex-direction: column;      width: 100%;    }    .tg-df-card-price-group {      display: flex;      flex-direction: row;      align-items: center;      gap: 8px;      margin-bottom: 12px;    }    .tg-df-card-price {      font-size: 16px;      font-weight: 700;      color: #dc2626; /* Red price */      line-height: 1;    }        .tg-df-card-msrp {      font-size: 13px;      color: var(--tg-df-text-muted);      text-decoration: line-through;    }    .tg-df-container .tg-df-card-cta {      display: flex;      align-items: center;      justify-content: center;      width: 100%;      box-sizing: border-box;      background-color: #1f69ff;      color: #ffffff;      font-size: 12px;      font-weight: 700;      text-transform: uppercase;      letter-spacing: 0.5px;      padding: 12px 16px;      border-radius: 0;      border: none;      cursor: pointer;      transition: background-color 0.2s ease;    }    .tg-df-card:hover .tg-df-card-cta,    .tg-df-card-cta:hover {      background-color: #1555cc;    }    .tg-df-container .tg-df-card-cta.tg-df-cta-savings-squad {      background-color: #3c8d0d;    }    .tg-df-card:hover .tg-df-card-cta.tg-df-cta-savings-squad,    .tg-df-card-cta.tg-df-cta-savings-squad:hover {      background-color: #2b6509;    }    /*       5. State & Skeleton Styles    */    .tg-df-message {      grid-column: 1 / -1;      text-align: center;      padding: 48px 24px;      color: var(--tg-df-text-muted);      font-size: 16px;      background: var(--tg-df-bg);      border: 1px solid var(--tg-df-border);      border-radius: 8px;    }    @keyframes tg-df-shimmer {      0% { background-position: -200% 0; }      100% { background-position: 200% 0; }    }    .tg-df-skeleton {      background: linear-gradient(90deg, var(--tg-df-bg-secondary) 25%, #e2e8f0 50%, var(--tg-df-bg-secondary) 75%);      background-size: 200% 100%;      animation: tg-df-shimmer 1.5s infinite;      border-radius: 4px;    }    .tg-df-skeleton-img {      width: 100%;      height: 100%;      position: absolute;      top: 0; left: 0;    }        .tg-df-skeleton-text {      height: 16px;      margin-bottom: 8px;      width: 100%;    }    .tg-df-skeleton-text.short { width: 40%; }    .tg-df-skeleton-text.title { height: 20px; margin-bottom: 16px; }    /* Editor Floating Bar & Elements */    .tg-df-editor-bar {      position: sticky;      top: 0;      z-index: 1000;      background: #111827;      color: #fff;      padding: 12px 16px;      border-radius: 8px;      margin-bottom: 16px;      display: flex;      align-items: center;      justify-content: space-between;      box-shadow: 0 4px 12px rgba(0,0,0,0.15);    }    .tg-df-editor-bar-text {      font-weight: 600;      font-size: 14px;    }    .tg-df-editor-copy-btn {      background: #10b981;      color: #fff;      padding: 6px 16px;      border: none;      border-radius: 4px;      font-weight: 600;      cursor: pointer;      display: flex;      align-items: center;      font-size: 13px;    }    .tg-df-editor-copy-btn:hover { background: #059669; }        .tg-df-deal-checkbox {      position: absolute;      top: 12px;      right: 12px;      z-index: 10;      width: 20px;      height: 20px;      cursor: pointer;      pointer-events: auto;    }    /*       6. Mobile List View (Stacks into a cleaner horizontal row/list)    */    @container tg-df (max-width: 599px) {      .tg-df-controls {        padding: 0 16px;      }            .tg-df-top-bar {        width: 100%;      }            .tg-df-settings-dropdown {        position: fixed;        top: auto;        bottom: 0;        left: 0;        right: 0;        width: 100%;        border-radius: 20px 20px 0 0;        padding: 24px;        box-shadow: 0 -8px 32px rgba(0,0,0,0.15);        z-index: 1000;        border: none;        border-top: 1px solid var(--tg-df-border);      }            .tg-df-settings-dropdown-backdrop.active {        background: rgba(0,0,0,0.4);      }            .tg-df-search-wrapper {        box-shadow: 0 0 16px rgba(0,0,0,0.08);      }            .tg-df-filters {        width: calc(100% + 32px);        margin: 0 -16px;        padding: 0 16px 4px 16px;        display: flex;        justify-content: flex-start;        gap: 8px;        flex-wrap: nowrap;        overflow-x: auto;        -webkit-overflow-scrolling: touch;        scrollbar-width: none;      }      .tg-df-filters::after {        content: "";        display: block;        flex: 0 0 8px;      }      .tg-df-filters::-webkit-scrollbar {        display: none;      }            .tg-df-sort-wrapper {        flex: 0 0 max(42%, 130px);        min-width: 0;      }      .tg-df-sort-wrapper.tg-df-price-range-wrapper {        flex: 0 0 auto;        min-width: max-content;      }            .tg-df-sort-select, .tg-df-filter-select {        width: 100%;        text-align: left;        padding: 10px 24px 10px 32px;        background-position: right 8px center;        text-overflow: ellipsis;        white-space: nowrap;        overflow: hidden;      }      .tg-df-sort-icon {        left: 10px;      }      .tg-df-grid:not(.layout-grid):not(.layout-row),      .tg-df-grid.layout-row {        grid-template-columns: 1fr;        gap: 16px;      }            .tg-df-grid.tg-df-grid-auto {        padding-top: 24px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card,      .tg-df-grid.layout-row .tg-df-card {        flex-direction: row;        align-items: stretch;        height: auto;        box-shadow: none; /* simple line on mobile if preferred, or keep */        border-bottom: 1px solid var(--tg-df-border);      }      .tg-df-grid.tg-df-grid-auto .tg-df-card:hover,      .tg-df-grid.layout-row .tg-df-card:hover {        box-shadow: none;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-image-box,      .tg-df-grid.layout-row .tg-df-card-image-box {        width: 120px;        min-width: 120px;        aspect-ratio: 3/4;        border-right: none;        padding: 12px;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-body,      .tg-df-grid.layout-row .tg-df-card-body {        padding: 12px;        justify-content: space-between;      }      .tg-df-grid.tg-df-grid-auto .tg-df-card-title,      .tg-df-grid.layout-row .tg-df-card-title {        font-size: 14px;        margin-bottom: 12px;        -webkit-line-clamp: 3;      }      /* Single column mobile grid override */      .tg-df-grid.layout-grid {        grid-template-columns: 1fr;        gap: 16px;      }      .tg-df-grid.layout-grid .tg-df-card-image-box {        padding: 12px;      }      .tg-df-grid.layout-grid .tg-df-card-body {        padding: 10px;      }      .tg-df-grid.layout-grid .tg-df-card-title {        font-size: 13px;        -webkit-line-clamp: 3;        margin-bottom: 8px;      }      .tg-df-grid.layout-grid .tg-df-card-price {        font-size: 14px;      }            .tg-df-card-footer {        flex-direction: column;        align-items: stretch;        gap: 0;        width: 100%;        min-width: 0;      }      .tg-df-card-merchant-pill {        margin-bottom: 4px;      }      .tg-df-card-price-group {        flex: 1 1 auto;        margin-bottom: 8px;      }      .tg-df-card-price {        font-size: 16px;      }      .tg-df-card-msrp {        display: block;       }      .tg-df-grid.layout-row .tg-df-card-cta,      .tg-df-container .tg-df-card-cta {        width: 100%;        max-width: none;        min-width: 0;        box-sizing: border-box;        padding: 8px 16px;        font-size: 12px;        flex: 0 0 auto;        text-align: center;        white-space: normal;        line-height: 1.2;      }    }    .tg-df-container.is-carousel {      min-height: 760px;      background-color: #E7F0FF;      padding: 0 0 24px 0;      border-radius: 24px;    }    .tg-df-container.is-carousel.hide-header-details {      min-height: 480px;    }    /*       7. Carousel View Mode    */    .tg-df-container .tg-df-carousel-host {      /* Layout is now handled by container wrapper */    }    .tg-df-container .tg-df-carousel-eyebrow {      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      padding: 24px 16px 0 16px;      display: none;    }    .tg-df-container .tg-df-carousel-query-title {      color: #011535;      font-size: 28px;      font-weight: 600;      padding: 0 16px 24px 16px;      line-height: 1.2;      display: none;    }    .tg-df-container .tg-df-carousel-blue-box {      background-color: transparent;      border-radius: 0;      padding: 24px 24px 0 24px;      margin: 0;      color: #1F69FF;          position: relative;      overflow: hidden;    }    .tg-df-container .tg-df-carousel-bg-circle-1 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-2 {      display: none;    }    .tg-df-container .tg-df-carousel-bg-circle-3 {      display: none;    }    .tg-df-container .tg-df-carousel-box-content {      position: relative;      z-index: 10;    }    .tg-df-container .tg-df-carousel-box-eyebrow {      background-color: transparent;      color: #1F69FF;      font-weight: 700;      font-size: 14px;      text-transform: uppercase;      letter-spacing: 1px;      display: inline-block;      padding: 0;      border-radius: 0;    }    .tg-df-container .tg-df-carousel-box-title {      font-size: 28px;      font-weight: 600;      line-height: 1.2;      margin-top: 8px;      color: #1e293b;    }    .tg-df-container .tg-df-countdown-wrapper {      position: absolute;      top: 0;      right: 0;      display: flex;      flex-direction: column;      align-items: flex-end;      gap: 12px;      transform: scale(0.67);      transform-origin: top right;    }    .tg-df-container .tg-df-countdown-title {      font-size: 16px;      text-align: center;      width: 100%;      font-weight: 600;      color: #011535;      margin: 0;    }    .tg-df-container .tg-df-countdown-blocks {      display: flex;      gap: 16px;    }    .tg-df-container .tg-df-countdown-item {      display: flex;      flex-direction: column;      align-items: center;      gap: 4px;    }    .tg-df-container .tg-df-countdown-box {      width: 59px;      height: 59px;      background: #03FE9E;      border-radius: 15px;      display: flex;      align-items: center;      justify-content: center;    }    .tg-df-container .tg-df-countdown-num {      font-family: 'Inter', sans-serif;      font-weight: 700;      font-size: 20px;      line-height: normal;      color: #011535;    }    .tg-df-container .tg-df-countdown-label {      font-family: 'Inter', sans-serif;      font-weight: 500;      font-size: 16px;      line-height: normal;      color: #1e293b;      text-transform: uppercase;    }    .tg-df-container .tg-df-carousel-box-subtitle {      font-size: 16px;      margin-top: 8px;      font-weight: 300;      color: #1e293b;      line-height: 24px;    }    .tg-df-container .tg-df-carousel-roundels-wrapper {      position: relative;      margin-top: 24px;      margin-left: -24px;      margin-right: -24px;    }    .tg-df-container .tg-df-carousel-roundels {      display: flex;      gap: 16px;      overflow-x: auto;            scrollbar-width: none;      padding-top: 12px;      padding-bottom: 24px;      padding-left: 24px;      padding-right: 24px;      margin-left: 0;      margin-right: 0;    }    .tg-df-container .tg-df-carousel-scroll-right {      position: absolute;      right: 8px;      top: 50%;      transform: translateY(-50%);      height: 36px;      width: 36px;      display: flex;      align-items: center;      justify-content: center;      border-radius: 50%;      background-color: #ffffff;      border: 1px solid #e2e8f0;      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);      color: #1F69FF;      cursor: pointer;      transition: all 0.2s;      margin-top: -4px;      z-index: 20;    }    .tg-df-container .tg-df-carousel-scroll-right:hover {      background-color: #f8fafc;      border-color: #cbd5e1;    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right {      right: 0;      background-color: rgba(255, 255, 255, 0.4);      border: none;      box-shadow: none;      backdrop-filter: blur(4px);      -webkit-backdrop-filter: blur(4px);    }    .tg-df-carousel-roundels-wrapper .tg-df-carousel-scroll-right:hover {      background-color: rgba(255, 255, 255, 0.6);      border: none;    }    .tg-df-container .tg-df-carousel-roundels::-webkit-scrollbar {      display: none;    }    .tg-df-container .tg-df-carousel-roundels::after {      content: "";      flex: 0 0 32px;    }    .tg-df-container .tg-df-roundel {      display: flex;      flex-direction: column;      align-items: center;      gap: 8px;      cursor: pointer;      min-width: 120px;      flex-shrink: 0;    }    .tg-df-container .tg-df-roundel-img-box {      width: 120px;      height: 120px;      border-radius: 50%;      background: white;      display: flex;      align-items: center;      justify-content: center;      overflow: hidden;      box-shadow: 0px 3px 14px 0px rgba(30, 41, 59, 0.08);      transition: box-shadow 0.2s;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-img-box {      box-shadow: 0 0 0 2px #E7F0FF, 0 0 0 4px #1F69FF;    }    .tg-df-container .tg-df-roundel:hover .tg-df-roundel-img-box img {      transform: scale(1.08);    }    .tg-df-container .tg-df-roundel-img-box img {      width: 100%;      height: 100%;      object-fit: contain;      padding: 10px;      box-sizing: border-box;      transition: transform 0.3s ease;    }    .tg-df-container .tg-df-roundel-label {      font-size: 13px;      font-weight: 400;      color: #1e293b;      text-align: center;      transition: font-weight 0.2s;    }    .tg-df-container .tg-df-roundel.active .tg-df-roundel-label {      font-weight: 700;    }    .tg-df-container .tg-df-carousel-filters-label {      font-size: 16px;      font-weight: 400;      color: #1e293b;      white-space: nowrap;      margin-right: 4px;    }    .tg-df-container .tg-df-carousel-filters-wrap {      display: flex;      align-items: center;      flex-wrap: nowrap;      gap: 8px;      margin-top: 8px;      overflow-x: auto;      scrollbar-width: none;      -webkit-overflow-scrolling: touch;      padding-bottom: 8px;      margin-left: -24px;      margin-right: -24px;      padding-left: 24px;      padding-right: 24px;    }    .tg-df-container .tg-df-carousel-filters-wrap::-webkit-scrollbar {      display: none;    }        .tg-df-container .tg-df-carousel-filter-btn img,    .tg-df-container .tg-df-carousel-filter-btn picture {      height: 20px;      width: 20px;      object-fit: contain;      object-position: center;      display: inline-flex;      align-items: center;      justify-content: center;      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn picture img {      margin-right: 0;      height: 100%;      width: 100%;    }    .tg-df-container .tg-df-carousel-filter-btn img.active-img,    .tg-df-container .tg-df-carousel-filter-btn picture:has(.active-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.inactive-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.inactive-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.inactive-img) {      display: none;    }    .tg-df-container .tg-df-carousel-filter-btn:hover img.active-img,    .tg-df-container .tg-df-carousel-filter-btn.active img.active-img,    .tg-df-container .tg-df-carousel-filter-btn:hover picture:has(.active-img),    .tg-df-container .tg-df-carousel-filter-btn.active picture:has(.active-img) {      display: inline-flex;    }    .tg-df-container .tg-df-carousel-filter-btn {      background: #ffffff;      border: 2px solid #1e293b;      color: #1e293b;      border-radius: 24px;      padding: 6px 16px;      font-size: 14px;      font-weight: 600;      cursor: pointer;      transition: all 0.2s;      flex-shrink: 0;      white-space: nowrap;      display: inline-flex;      align-items: center;      justify-content: center;      min-height: 36px;      box-sizing: border-box;    }    .tg-df-container .tg-df-carousel-filter-btn svg {      margin-right: 6px;    }    .tg-df-container .tg-df-carousel-filter-btn:hover {      background: #1e293b;      color: white;      border-color: #1e293b;    }    .tg-df-container .tg-df-carousel-filter-btn.active {      background: #1e293b;      color: white;      border-color: #1e293b;    }        .tg-df-grid.carousel-compact {      display: flex;      flex-wrap: nowrap;      overflow-x: auto;      gap: 16px;      padding: 16px 24px;      align-items: stretch;      scrollbar-width: none;    }    .tg-df-grid.carousel-compact::after {      content: "";      flex: 0 0 32px;    }    .tg-df-grid-wrapper {      position: relative;    }    .tg-df-grid.carousel-compact::-webkit-scrollbar {      display: none;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card {      flex: 0 0 auto;      width: 100px;      border-radius: 15px;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      border: 2px solid #1e293b;      background: white;      color: #1e293b;      display: flex;      flex-direction: column;      justify-content: center;      align-items: center;      font-weight: 600;      font-size: 14px;      cursor: pointer;      padding: 16px;      text-align: center;      transition: all 0.2s;    }    .tg-df-grid.carousel-compact .tg-df-load-more-card:hover {      background: #1e293b;      color: white;    }    .tg-df-grid.carousel-compact .tg-df-card {      flex: 0 0 auto;      width: 200px;      min-height: auto;      height: auto;      display: flex;      flex-direction: column;      border-radius: 15px;      border: none;      box-shadow: 0 0 16px rgba(0,0,0,0.08);      overflow: visible;    }    .tg-df-grid.carousel-compact .tg-df-card-image-box {      padding: 12px;      background-color: transparent;      border-radius: 15px 15px 0 0;      height: 130px;    }    .tg-df-grid.carousel-compact .tg-df-card-image {      mix-blend-mode: normal;    }    .tg-df-grid.carousel-compact .tg-df-card-discount-badge {      border-radius: 0;      top: 0px;      left: 0px;      padding: 4px 8px;      font-size: 11px;    }    .tg-df-grid.carousel-compact .tg-df-card-body {      padding: 8px 12px 12px 12px;    }    .tg-df-grid.carousel-compact .tg-df-card-title {      font-size: 14px;      font-weight: 400;      -webkit-line-clamp: 2;      margin-bottom: 8px;      color: #011535;    }    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):not(:has(.tg-df-tag-prime)):not(:has(.tg-df-coupon-wrapper:not([style*="none"]))) > .tg-df-card-title,    .tg-df-grid.carousel-compact .tg-df-card-body:not(:has(.tg-df-card-stars)):has(> .tg-df-card-title:first-child) > .tg-df-card-title {      -webkit-line-clamp: 3;    }    .tg-df-grid.carousel-compact .tg-df-card-cta {      border-radius: 5px;      padding: 8px 10px;      margin-top: 4px;      background-color: #1F69FF;    }    .tg-df-grid.carousel-compact .tg-df-card-price-group {      margin-bottom: 2px;    }    .tg-df-grid.carousel-compact .tg-df-card-merchant-pill {      margin-bottom: 2px;    }    @container tg-df (max-width: 599px) {      .tg-df-container .tg-df-carousel-blue-box-title {        font-size: 24px;      }      .tg-df-container .tg-df-countdown-title {        display: none;      }      .tg-df-container .tg-df-countdown-wrapper {        position: absolute;        top: 0;        right: 0;        align-items: flex-end;        transform: scale(0.45);        transform-origin: top right;      }      .tg-df-container .tg-df-roundel {        min-width: 88px;      }      .tg-df-container .tg-df-roundel-img-box {        width: 88px;        height: 88px;      }    }    /* REPLICA BLOCK STYLES */    .tg-df-grid.layout-replica-2 { grid-template-columns: repeat(2, 1fr) !important; gap: 20px; }    .tg-df-grid.layout-replica-1 { grid-template-columns: 1fr !important; gap: 20px; }        .tg-df-container .hawk-deal-widget-container { border-bottom: 1px solid #e5e7eb; display: flex; flex-direction: column; margin: 0; padding: 20px 0; box-sizing: border-box; font-family: inherit; }    .tg-df-container .hawk-deal-widget-wrap { display: flex; flex-direction: row; align-items: flex-start; width: 100%; gap: 24px; }    .tg-df-container .hawk-deal-widget-image-container { display: flex; flex-shrink: 0; justify-content: center; width: 160px; height: 160px; align-items: center; background: white; margin-bottom: 0px; }    .tg-df-container .hawk-deal-widget-title-product-title { color: #111827; font-size: 18px; font-weight: 700; line-height: 1.4; display: inline; }    .tg-df-container .hawk-deal-widget-title-price { font-size: 18px; font-weight: 700; line-height: 1.4; white-space: nowrap; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-price-now { font-weight: 700; }    .tg-df-container .hawk-deal-widget-title-retailer-price:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-title-retailer { font-size: 18px; font-weight: 700; line-height: 1.4; color: #2563eb; }    .tg-df-container .hawk-deal-widget-title-was-price { color: #dc2626; font-size: 16px; font-weight: 500; line-height: 1.4; text-decoration: line-through; white-space: nowrap; margin-left: 8px; margin-right: 8px; }    .tg-df-container .hawk-deal-widget-text-body-container { position: relative; width: 100%; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-text-body-main { font-size: 16px; width: 100%; margin-bottom: 12px; }    .tg-df-container .hawk-deal-widget-text-body-description { display: block; font-size: 15px; margin-top: 12px; color: #4b5563; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-body-description p { margin: 0; line-height: 1.6; }    .tg-df-container .hawk-deal-widget-text-cta-container { display: flex; flex-direction: column; gap: 12px; width: 100%; flex: 1; min-width: 0; box-sizing: border-box; }    .tg-df-container .hawk-deal-widget-footer { display: flex; justify-content: flex-end; width: 100%; margin-top: auto; }    .tg-df-container .hawk-deal-widget-button-wrapper { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; width: 100%; }    .tg-df-container .hawk-deal-widget-preferred-partner-wrapper { display: flex; flex-direction: row; }        @container tg-df (min-width: 600px) {      .tg-df-mobile-only { display: none !important; }    }    @container tg-df (max-width: 599px) {      .tg-df-desktop-only { display: none !important; }      .tg-df-grid.layout-replica-2 { grid-template-columns: 1fr !important; }      .tg-df-grid.savings-squad-cards { grid-template-columns: 1fr !important; display: flex; flex-direction: column; }    }    .tg-df-grid.savings-squad-cards .tg-df-card-title {      -webkit-line-clamp: unset !important;      display: block !important;      overflow: visible !important;    }    @container tg-df (max-width: 500px) {      .tg-df-container .hawk-deal-widget-wrap { display: block; }      .tg-df-container .hawk-deal-widget-image-container { display: block; float: left; margin: 0 16px 8px 0; width: 120px; max-width: 120px; height: auto; align-items: normal; justify-content: normal; }      .tg-df-container .hawk-deal-widget-text-cta-container { display: block; text-align: left; }      .tg-df-container .hawk-deal-widget-footer { display: block; margin-top: 16px; clear: both; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-deal-widget-button-wrapper .hawk-deal-widget-preferred-partner-wrapper { display: block; width: 100%; }      .tg-df-container .hawk-affiliate-link-deal-button { box-sizing: border-box !important; display: flex !important; max-width: none !important; width: 100% !important; margin: 0 !important; }    }        .tg-df-container .hawk-affiliate-link-deal-button {       align-items: center; background-color: #5aaf0b; box-sizing: border-box; color: #ffffff !important; display: flex; font-size: 14px; font-weight: 700; justify-content: center; letter-spacing: 0.5px; line-height: 1; min-width: 160px; padding: 14px 24px; text-align: center; text-decoration: none; text-transform: uppercase; width: 100%; word-break: normal; border-radius: 4px; border: 0; transition: background-color 0.2s;     }    .tg-df-container .hawk-affiliate-link-deal-button:hover { background-color: #4a9109; text-decoration: none; }    .tg-df-container .hawk-lazy-image-deal-widget { display: block; height: auto; margin: auto; max-height: 160px; max-width: 100%; mix-blend-mode: multiply; object-fit: contain; }    .tg-df-container .hawk-deal-widget-text-cta-container a { color: #2563eb; text-decoration: none; display: inline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover { text-decoration: underline; }    .tg-df-container .hawk-deal-widget-text-cta-container a:has(.hawk-deal-widget-title-product-title) { color: #111827; }    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-product-title,    .tg-df-container .hawk-deal-widget-text-cta-container a:hover .hawk-deal-widget-title-retailer-price { text-decoration: underline; }  \x3C/style>  \x3C!-- Widget Container --\x3E  \x3Cdiv class="tg-df-container" id="signal-deals-finder-root">    \x3C!-- Editor Floating Bar --\x3E    \x3Cdiv class="tg-df-editor-bar" id="tg-df-editor-bar" style="display:none;">      \x3Cdiv class="tg-df-editor-bar-text" style="display: flex; align-items: center;">        \x3Cspan id="tg-df-selected-count">0\x3C/span>\x26nbsp;Deals Selected        \x3Cbutton class="tg-df-editor-clear-btn" id="tg-df-editor-clear" type="button" style="margin-left: 12px; font-size: 13px; color: #9ca3af; background: none; border: none; cursor: pointer; text-decoration: underline;">Clear All\x3C/button>      \x3C/div>      \x3Cbutton class="tg-df-editor-copy-btn" id="tg-df-editor-copy" type="button">        \x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">\x3C/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">\x3C/path>\x3C/svg>        Copy to CMS      \x3C/button>    \x3C/div>    \x3Cdiv class="tg-df-carousel-host" id="tg-df-carousel-host" style="display: none;">      \x3Cdiv class="tg-df-carousel-eyebrow">DEAL FINDER\x3C/div>      \x3Cdiv class="tg-df-carousel-query-title" id="tg-df-carousel-title-label">Best Deals\x3C/div>            \x3Cdiv class="tg-df-carousel-blue-box">        \x3Cdiv class="tg-df-carousel-bg-circle-1" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-2" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-bg-circle-3" aria-hidden="true">\x26nbsp;\x3C/div>        \x3Cdiv class="tg-df-carousel-box-content">          \x3Cdiv class="tg-df-countdown-wrapper" id="tg-df-countdown-wrapper" style="display:none;">            \x3Cdiv class="tg-df-countdown-title" id="tg-df-countdown-title">Prime Day starts in\x3C/div>            \x3Cdiv class="tg-df-countdown-blocks">              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-days">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">DAYS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-hrs">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">HRS\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-min">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">MIN\x3C/div>\x3C/div>              \x3Cdiv class="tg-df-countdown-item">\x3Cdiv class="tg-df-countdown-box">\x3Cdiv class="tg-df-countdown-num" id="tg-df-cd-sec">0\x3C/div>\x3C/div>\x3Cdiv class="tg-df-countdown-label">SEC\x3C/div>\x3C/div>            \x3C/div>          \x3C/div>          \x3Cdiv class="tg-df-carousel-box-eyebrow">DEAL FINDER\x3C/div>          \x3Cdiv class="tg-df-carousel-box-title">Find Deals Fast\x3C/div>          \x3Cdiv class="tg-df-carousel-box-subtitle">The latest deals from the biggest retailers, all in one place\x3C/div>                    \x3Cdiv class="tg-df-carousel-roundels-wrapper">          \x3Cdiv class="tg-df-carousel-roundels">            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>            \x3Cdiv class="tg-df-roundel tg-df-roundel-skeleton">\x3Cdiv class="tg-df-roundel-img-box tg-df-skeleton">\x3C/div>\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="margin: 4px auto 0 auto; height: 13px; width: 48px;">\x3C/div>\x3C/div>          \x3C/div>          \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.previousElementSibling.scrollBy({left: 200, behavior: 'smooth'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-carousel-filters-wrap">                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="0">All\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_lightning">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-ot="amazon_prime">              \x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>            \x3Cbutton class="tg-df-carousel-filter-btn" data-d="10">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 10% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="15">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 15% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-d="25">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>            Min 25% off\x3C/button>                      \x3Cbutton class="tg-df-carousel-filter-btn" data-pr="under50">              \x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>            Under $50\x3C/button>\n        \x3C/div>\n      \x3C/div>\n    \x3C/div>          \x3C!-- Search & Filter Controls --\x3E    \x3Cdiv class="tg-df-controls" id="tg-df-controls" style="display:flex;">      \x3Cdiv class="tg-df-top-bar">        \x3Cdiv class="tg-df-search-wrapper">          \x3Cinput type="text" class="tg-df-search-input" placeholder="Search for deals, products, or brands...">          \x3Cbutton type="button" class="tg-df-search-btn" aria-label="Search">              \x3Csvg class="tg-df-search-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">                \x3Cpath d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>              \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-autocomplete-dropdown" id="tg-df-autocomplete">\x3C/div>        \x3C/div>                \x3Cdiv class="tg-df-settings-wrapper">          \x3Cbutton type="button" class="tg-df-settings-btn" aria-label="Settings" id="tg-df-settings-toggle">            \x3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">                \x3Cpath d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.73 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .43-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.49-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/>            \x3C/svg>          \x3C/button>          \x3Cdiv class="tg-df-settings-dropdown-backdrop" id="tg-df-settings-backdrop">\x3C/div>          \x3Cdiv class="tg-df-settings-dropdown" id="tg-df-settings-panel">            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Search Region\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-region-select">                \x3Coption value="auto">🌍 Auto-detect\x3C/option>                \x3Coption value="US">🇺🇸 United States (US)\x3C/option>                \x3Coption value="GB">🇬🇧 United Kingdom (UK)\x3C/option>                \x3Coption value="CA">🇨🇦 Canada (CA)\x3C/option>                \x3Coption value="AU">🇦🇺 Australia (AU)\x3C/option>                \x3Coption value="DE">🇩🇪 Germany (DE)\x3C/option>                \x3Coption value="FR">🇫🇷 France (FR)\x3C/option>                \x3Coption value="IT">🇮🇹 Italy (IT)\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Retailer\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-retailer-select">                \x3Coption value="">All Retailers\x3C/option>                \x3Coption value="Amazon">Amazon\x3C/option>                \x3Coption value="Walmart">Walmart\x3C/option>                \x3Coption value="Best Buy">Best Buy\x3C/option>                \x3Coption value="Target">Target\x3C/option>                \x3Coption value="John Lewis">John Lewis\x3C/option>                \x3Coption value="Currys">Currys\x3C/option>                \x3Coption value="Argos">Argos\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Offer Type\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-offer-type-select">                \x3Coption value="">All Offers\x3C/option>                \x3Coption value="amazon_prime">Amazon Prime\x3C/option>                \x3Coption value="recommended_promo">Recommended Promo\x3C/option>                \x3Coption value="amazon_lightning">Amazon Lightning Deal\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">Result Count\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-rows-select">                \x3Coption value="3">3 Items\x3C/option>                \x3Coption value="4">4 Items\x3C/option>                \x3Coption value="6">6 Items\x3C/option>                \x3Coption value="12" selected>12 Items\x3C/option>                \x3Coption value="24">24 Items\x3C/option>                \x3Coption value="48">48 Items\x3C/option>              \x3C/select>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Deal Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Only show products with active offers or previous prices (was_price)\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-deal-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item tg-df-dl-row">              \x3Cdiv>                \x3Cspan class="tg-df-dl-row-text">Editor Mode\x3C/span>                \x3Cspan class="tg-df-dl-row-subtext">Enable multi-select to copy deals to CMS\x3C/span>              \x3C/div>              \x3Clabel class="tg-df-toggle">                \x3Cinput type="checkbox" id="tg-df-editor-mode">                \x3Cspan class="tg-df-slider">\x3C/span>              \x3C/label>            \x3C/div>            \x3Cdiv class="tg-df-setting-item">              \x3Clabel class="tg-df-setting-label">View Mode\x3C/label>              \x3Cselect class="tg-df-region-select" id="tg-df-view-mode-select">                \x3Coption value="auto">Auto Collection\x3C/option>                \x3Coption value="carousel">Carousel\x3C/option>                \x3Coption value="savings_squad">Savings Squad\x3C/option>                \x3Coption value="grid">Grid (Columns)\x3C/option>                \x3Coption value="row">Row (List)\x3C/option>              \x3C/select>            \x3C/div>          \x3C/div>        \x3C/div>      \x3C/div>      \x3Cdiv class="tg-df-filters">        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-category-filter-wrapper" style="display: none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-category-filter" aria-label="Category">            \x3Coption value="all">All Categories\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-multiselect-container" id="tg-df-brand-filter-wrapper" style="display:none;">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M4.25 5.61C6.27 8.2 10 13 10 13v6c0 .55.45 1 1 1h2c.55 0 1-.45 1-1v-6s3.72-4.8 5.74-7.39A.998.998 0 0 0 18.95 4H5.04c-.83 0-1.3.95-.79 1.61z"/>          \x3C/svg>          \x3Cdiv class="tg-df-filter-select tg-df-multiselect-trigger" id="tg-df-brand-trigger" tabindex="0">            Any Brand          \x3C/div>          \x3Cdiv class="tg-df-multiselect-dropdown" id="tg-df-brand-dropdown">            \x3C!-- Populated via script --\x3E          \x3C/div>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M9 3L5 6.99h3V14h2V6.99h3L9 3zm7 14.01V10h-2v7.01h-3L15 21l4-3.99h-3z"/>          \x3C/svg>          \x3Cselect class="tg-df-sort-select" aria-label="Sort Deals">            \x3Coption value="date_desc">Newest First\x3C/option>            \x3Coption value="best_match">Sort by: Match\x3C/option>            \x3Coption value="price_asc">Price Low to High\x3C/option>            \x3Coption value="price_desc">Price High to Low\x3C/option>            \x3Coption value="discount_desc">Biggest Discount\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper tg-df-price-range-wrapper" id="tg-df-custom-price-wrapper" style="display: flex; align-items:center; justify-content:center; padding: 10px 20px; gap: 8px; border: 1px solid var(--tg-df-border); border-radius: 100px; background-color: var(--tg-df-bg);">          \x3Cspan style="font-size:14px; font-weight:500; color:var(--tg-df-text-primary);">Price\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-min" placeholder="Min" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">          \x3Cspan style="color:var(--tg-df-text-muted)">-\x3C/span>          \x3Cinput type="number" class="tg-df-price-input" id="tg-df-custom-price-max" placeholder="Max" style="width: 48px; background: transparent; border: none; color: var(--tg-df-text-primary); outline: none; font-size: 14px; text-align: center; padding: 0;">        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-legacy-price-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-price-filter" aria-label="Filter Prices">            \x3Coption value="all">All Prices\x3C/option>            \x3Coption value="under50">Under $50\x3C/option>            \x3Coption value="50_100">$50 - $100\x3C/option>            \x3Coption value="100_200">$100 - $200\x3C/option>            \x3Coption value="200_500">$200 - $500\x3C/option>            \x3Coption value="over500">Over $500\x3C/option>          \x3C/select>        \x3C/div>        \x3Cdiv class="tg-df-sort-wrapper" id="tg-df-discount-filter-wrapper">          \x3Csvg class="tg-df-sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">            \x3Cpath d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/>          \x3C/svg>          \x3Cselect class="tg-df-filter-select" id="tg-df-discount-filter" aria-label="Discount Amount">            \x3Coption value="all">Any discount\x3C/option>            \x3Coption value="5">Min 5%\x3C/option>            \x3Coption value="10">Min 10%\x3C/option>            \x3Coption value="15">Min 15%\x3C/option>            \x3Coption value="20">Min 20%\x3C/option>            \x3Coption value="25">Min 25%\x3C/option>            \x3Coption value="30">Min 30%\x3C/option>            \x3Coption value="40">Min 40%\x3C/option>            \x3Coption value="50">Min 50%\x3C/option>            \x3Coption value="60">Min 60%\x3C/option>            \x3Coption value="70">Min 70%\x3C/option>          \x3C/select>        \x3C/div>      \x3C/div>    \x3C/div>    \x3C!-- Deals Grid Wrapper --\x3E    \x3Cdiv class="tg-df-grid-wrapper tg-df-carousel-cards-wrapper" id="tg-df-grid-wrapper">      \x3Cdiv class="tg-df-grid" id="tg-df-grid">        \x3C!-- Content populated by JavaScript --\x3E      \x3C/div>    \x3C/div>        \x3C!-- Vouchers Modal --\x3E    \x3Cdiv class="tg-df-modal-backdrop" id="tg-df-vouchers-modal">      \x3Cdiv class="tg-df-modal">        \x3Cdiv class="tg-df-modal-header">          \x3Ch3 class="tg-df-modal-title" id="tg-df-vouchers-title">Available Coupons & Deals\x3C/h3>          \x3Cbutton class="tg-df-modal-close" id="tg-df-vouchers-close">            \x3Csvg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">              \x3Cline x1="18" y1="6" x2="6" y2="18">\x3C/line>              \x3Cline x1="6" y1="6" x2="18" y2="18">\x3C/line>            \x3C/svg>          \x3C/button>        \x3C/div>        \x3Cdiv class="tg-df-modal-body" id="tg-df-vouchers-content">          \x3C!-- Vouchers injected here --\x3E        \x3C/div>      \x3C/div>    \x3C/div>  \x3C/div>`;      if (!template) {        template = document.createElement('template');        template.innerHTML = rawTemplate;      }      let shadowRoot = null;      if (hostContainer && template) {        hostContainer.setAttribute('data-initialized', 'true');        shadowRoot = hostContainer.attachShadow({ mode: 'open' });        shadowRoot.appendChild(template.content.cloneNode(true));      }      class DealsFinderWidget {        constructor(config) {          this.rootNode = config.rootNode || document;          this.hostContainer = config.hostContainer || null;          this.rootId = config.rootId || 'signal-deals-finder-root';          this.root = this.rootNode.querySelector('#' + this.rootId);          if (!this.root) return;          this.widgetId = (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'widget-' + Date.now() + '-' + Math.random().toString(36).slice(2);          this.grid = this.root.querySelector('#tg-df-grid');          this.tagsContainer = this.root.querySelector('#tg-df-tags-container');          this.categoryFilter = this.root.querySelector('#tg-df-category-filter');          this.categoryFilterWrapper = this.root.querySelector('#tg-df-category-filter-wrapper');          this.searchInput = this.root.querySelector('.tg-df-search-input');          this.autocompleteDropdown = this.root.querySelector('#tg-df-autocomplete');          this.sortSelect = this.root.querySelector('.tg-df-sort-select');          this.searchBtn = this.root.querySelector('.tg-df-search-btn');                    this.settingsToggle = this.root.querySelector('#tg-df-settings-toggle');          this.settingsPanel = this.root.querySelector('#tg-df-settings-panel');          this.settingsBackdrop = this.root.querySelector('#tg-df-settings-backdrop');          this.regionSelect = this.root.querySelector('#tg-df-region-select');          this.retailerSelect = this.root.querySelector('#tg-df-retailer-select');          this.offerTypeSelect = this.root.querySelector('#tg-df-offer-type-select');          this.viewModeSelect = this.root.querySelector('#tg-df-view-mode-select');          this.rowsSelect = this.root.querySelector('#tg-df-rows-select');          this.dealModeToggle = this.root.querySelector('#tg-df-deal-mode');          this.editorModeToggle = this.root.querySelector('#tg-df-editor-mode');          this.priceFilter = this.root.querySelector('#tg-df-price-filter');          this.discountFilter = this.root.querySelector('#tg-df-discount-filter');                    this.editorBar = this.root.querySelector('#tg-df-editor-bar');          this.editorSelectedCount = this.root.querySelector('#tg-df-selected-count');          this.editorCopyBtn = this.root.querySelector('#tg-df-editor-copy');          this.editorClearBtn = this.root.querySelector('#tg-df-editor-clear');                    this.apiUrl = 'https://search-api.fie.future.net.uk/widget.php';          this.deals = [];          this.displayLimit = 12;          this.airedaleArticles = null;          this.airedaleTags = [];          this.airedaleTagCounts = {};          this.activeDealTag = null;          this.selectedBrands = [];          this.currentQuery = '';          this.editorMode = this.hostContainer ? this.hostContainer.hasAttribute('data-editor-mode') : false;          this.viewModeOverride = this.hostContainer ? this.hostContainer.getAttribute('data-view-mode') : null;          this.selectedDeals = new Map();                    this.brandFilterWrapper = this.root.querySelector('#tg-df-brand-filter-wrapper');          this.brandTrigger = this.root.querySelector('#tg-df-brand-trigger');          this.brandDropdown = this.root.querySelector('#tg-df-brand-dropdown');                    this.customPriceWrapper = this.root.querySelector('#tg-df-custom-price-wrapper');          this.customPriceMin = this.root.querySelector('#tg-df-custom-price-min');          this.customPriceMax = this.root.querySelector('#tg-df-custom-price-max');          this.legacyPriceWrapper = this.root.querySelector('#tg-df-legacy-price-wrapper');          this.discountFilterWrapper = this.root.querySelector('#tg-df-discount-filter-wrapper');          this.initResizeObserver();          this.init();            if (['carousel', 'carousel-compact', 'auto', 'grid', 'row'].includes(this.getViewMode())) { this.loadCarouselSpreadsheet(); }        }        getViewMode() {          if (this.viewModeOverride && (!this.editorMode || !this.viewModeSelect)) {            return this.viewModeOverride;          }          return (this.viewModeSelect && this.viewModeSelect.value) ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');        }        applyLayoutMode() {          if (!this.grid) return;          const mode = this.getViewMode();          this.grid.classList.remove('layout-row', 'layout-grid', 'tg-df-grid-auto', 'carousel-compact', 'layout-replica-1', 'layout-replica-2');                    const carouselHost = this.root.querySelector('#tg-df-carousel-host');          const controlsDiv = this.root.querySelector('#tg-df-controls');          if (mode === 'carousel' || mode === 'auto' || mode === 'grid' || mode === 'row') {             if (mode === 'carousel') this.grid.classList.add('carousel-compact');             if (carouselHost) carouselHost.style.display = 'block';             if (controlsDiv) controlsDiv.style.display = 'none';             if (this.root.classList.contains('tg-df-container') && mode === 'carousel') {               this.root.classList.add('is-carousel');             } else if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          } else {             if (carouselHost) carouselHost.style.display = 'none';             if (controlsDiv) controlsDiv.style.display = 'flex';             if (this.root.classList.contains('tg-df-container')) {               this.root.classList.remove('is-carousel');             }          }          if (mode === 'grid') {            this.grid.classList.add('layout-grid');          } else if (mode === 'row') {            this.grid.classList.add('layout-row');          } else if (mode === 'savings_squad') {            this.grid.classList.add('tg-df-grid-auto', 'savings-squad-cards');          } else if (mode !== 'carousel') {            this.grid.classList.add('tg-df-grid-auto');          }                    const settingsWrapper = this.root.querySelector('.tg-df-settings-wrapper');          if (settingsWrapper) {            settingsWrapper.style.display = mode === 'auto' ? 'none' : 'block';          }          if (this.customPriceWrapper) {             this.customPriceWrapper.style.display = mode === 'auto' ? 'flex' : 'none';          }          if (this.legacyPriceWrapper) {             this.legacyPriceWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }          if (this.discountFilterWrapper) {             this.discountFilterWrapper.style.display = mode === 'auto' ? 'none' : 'flex';          }        }        initResizeObserver() {          try {            if (window.parent === window) return;          } catch (e) {            // cross origin frame check threw          }          const emitHeight = () => {            try {              const height = document.documentElement.scrollHeight || document.body.scrollHeight;              const msg = { type: 'embed-size', height: height };              if (window.parent && window.parent !== window) {                window.parent.postMessage(msg, '*');                window.parent.postMessage(JSON.stringify({ ...msg, sentinel: 'amp' }), '*');              }            } catch (e) {}          };                    if (window.ResizeObserver) {            try {              const ro = new ResizeObserver(() => emitHeight());              ro.observe(document.body);              if (this.root) ro.observe(this.root);            } catch(e){ console.warn(e); }          }          window.addEventListener('resize', emitHeight);          setTimeout(emitHeight, 300);        }        initCountdown() {          this.cdWrapper = this.root.querySelector('#tg-df-countdown-wrapper');                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          this.showCountdown = params.get('show_countdown') === 'true';          const showHeaderDetails = params.get('show_header_details') !== 'false';          const eyebrow = this.root.querySelector('.tg-df-carousel-box-eyebrow');          const title = this.root.querySelector('.tg-df-carousel-box-title');          const subtitle = this.root.querySelector('.tg-df-carousel-box-subtitle');          if (!showHeaderDetails) {            let containerElement = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (containerElement) containerElement.classList.add('hide-header-details');            if (eyebrow) eyebrow.style.display = 'none';            if (title) title.style.display = 'none';            if (subtitle) subtitle.style.display = 'none';          }          if (!this.cdWrapper) return;          this.cdTitle = this.root.querySelector('#tg-df-countdown-title');          this.cdDays = this.root.querySelector('#tg-df-cd-days');          this.cdHrs = this.root.querySelector('#tg-df-cd-hrs');          this.cdMin = this.root.querySelector('#tg-df-cd-min');          this.cdSec = this.root.querySelector('#tg-df-cd-sec');          this.updateCountdown();          this.cdInterval = setInterval(() => this.updateCountdown(), 1000);        }        updateCountdown() {          if (!this.cdWrapper) return;          if (!this.showCountdown) {            this.cdWrapper.style.display = 'none';            return;          }          const area = this.getAreaCode();          let offset = '-04:00';          if (['DE', 'FR', 'IT', 'ES', 'NL'].includes(area)) {             offset = '+02:00';          } else if (['GB', 'IE', 'UK'].includes(area)) {             offset = '+01:00';          }          const startTime = new Date('2026-06-23T00:00:00' + offset).getTime();          const endTime = new Date('2026-06-26T00:00:00' + offset).getTime();          const now = Date.now();          let targetTime = 0;          if (now < startTime) {             targetTime = startTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day starts in';             this.cdWrapper.style.display = 'flex';          } else if (now < endTime) {             targetTime = endTime;             if (this.cdTitle) this.cdTitle.textContent = 'Prime Day ends in';             this.cdWrapper.style.display = 'flex';          } else {             this.cdWrapper.style.display = 'none';             if (this.cdInterval) clearInterval(this.cdInterval);             return;          }          const diff = Math.max(0, targetTime - now);          const d = Math.floor(diff / (1000 * 60 * 60 * 24));          const h = Math.floor((diff / (1000 * 60 * 60)) % 24);          const m = Math.floor((diff / 1000 / 60) % 60);          const s = Math.floor((diff / 1000) % 60);          if (this.cdDays) this.cdDays.textContent = d;          if (this.cdHrs) this.cdHrs.textContent = h;          if (this.cdMin) this.cdMin.textContent = m;          if (this.cdSec) this.cdSec.textContent = s;        }        init() {          this.initCountdown();          try {            initAnalytics();          } catch (e) {            console.warn('Deals Widget Analytics Error:', e);          }                    this.bindEvents();                    let initialQuery = '';                    let searchSource = window.location.search;          if (this.hostContainer && this.hostContainer.hasAttribute('data-widget-config')) {            searchSource = this.hostContainer.getAttribute('data-widget-config');          } else if (typeof window !== 'undefined' && window.__WIDGET_CONFIG__) {            searchSource = window.__WIDGET_CONFIG__;          }          const params = new URLSearchParams(searchSource);          let initialViewMode = params.get('view_mode');          if (!params.has('search') && !params.has('q') && !params.has('query') && initialViewMode !== 'savings_squad') {             initialQuery = 'Everything';             if (this.discountFilter && !params.has('min_discount_ratio')) {               this.discountFilter.value = '5';             }          }                    if (this.regionSelect) {            this.regionSelect.value = params.get('region') || 'auto';            this.updatePriceDropdownCurrency();          }                    if (this.retailerSelect && params.has('retailer')) {            this.retailerSelect.value = params.get('retailer');          }                    if (params.has('brands')) {            const b = params.get('brands');            if (b) {              this.selectedBrands = b.split(',');            }          }                    if (this.offerTypeSelect && params.has('offer_type')) {            this.offerTypeSelect.value = params.get('offer_type');          }          if (params.has('bg_color')) {            const bg = params.get('bg_color');            if (bg === 'white') {              this.root.style.setProperty('background-color', '#ffffff', 'important');            } else if (bg === 'transparent') {              this.root.style.setProperty('background-color', 'transparent', 'important');            } else if (bg === 'light_blue') {              this.root.style.setProperty('background-color', '#E7F0FF', 'important');            }          } else {             this.root.style.removeProperty('background-color');          }                    if (params.has('view_mode')) {            if (this.viewModeSelect) {              this.viewModeSelect.value = params.get('view_mode');            } else {              this.viewModeOverride = params.get('view_mode');            }          }          if (this.rowsSelect && params.has('rows')) {            this.rowsSelect.value = params.get('rows');          }          if (params.has('price')) {            const priceVal = params.get('price');            if (this.priceFilter) {               // Try assigning it directly to select. If it's not present implicitly ignores               this.priceFilter.value = priceVal;            }            if (priceVal.includes('_')) {               const parts = priceVal.split('_');               if (this.customPriceMin && parts[0]) this.customPriceMin.value = parts[0];               if (this.customPriceMax && parts[1]) this.customPriceMax.value = parts[1];            }          }          if (this.discountFilter && params.has('min_discount_ratio')) {            // Need to convert back from ratio (e.g. 0.8) to select value (e.g. "20")            const ratioStr = params.get('min_discount_ratio');            const ratioFloat = parseFloat(ratioStr);            if (!isNaN(ratioFloat)) {               const percentage = Math.round((1 - ratioFloat) * 100);               this.discountFilter.value = percentage.toString();            }          }          if (this.sortSelect) {            this.sortSelect.value = params.get('sort') || 'discount_desc';          }          if (this.dealModeToggle && params.has('deal_mode')) {            this.dealModeToggle.checked = params.get('deal_mode') === 'true' || params.get('deal_mode') === '1';          }                    // Re-apply layout after params have updated control values          this.applyLayoutMode();                    if (params.get('search')) {            initialQuery = params.get('search');          } else if (params.get('q')) {            initialQuery = params.get('q');          } else if (params.get('query')) {            initialQuery = params.get('query');          }                    this.currentQuery = initialQuery;          if (this.searchInput) {            this.searchInput.value = this.currentQuery;          }                    if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {            this.fetchDeals(this.currentQuery);          } else {            this.render();          }        }        updatePriceDropdownCurrency() {          if (!this.priceFilter || !this.regionSelect) return;          const currencySymbols = {            'US': '$',            'GB': '£',            'CA': '$CA',            'AU': '$AU',            'DE': '€',            'FR': '€',            'IT': '€',          };          const area = this.getAreaCode();          const cur = currencySymbols[area || 'US'] || '$';                    const options = this.priceFilter.options;          for (let i = 0; i < options.length; i++) {            const opt = options[i];            if (opt.value === 'all') {              opt.innerText = 'All Prices';            } else if (opt.value === 'under50') {              opt.innerText = `Under ${cur}50`;            } else if (opt.value === '50_100') {              opt.innerText = `${cur}50 - ${cur}100`;            } else if (opt.value === '100_200') {              opt.innerText = `${cur}100 - ${cur}200`;            } else if (opt.value === '200_500') {              opt.innerText = `${cur}200 - ${cur}500`;            } else if (opt.value === 'over500') {              opt.innerText = `Over ${cur}500`;            }          }        }        populateBrandDropdown(values) {          if (!this.brandDropdown || !this.brandFilterWrapper) return;          this.brandFilterWrapper.style.display = 'flex'; // show the wrapper                    let html = '';          const allChecked = this.selectedBrands.length === 0 ? 'checked' : '';          const _div = '<' + '/div>';          const _span = '<' + '/span>';          html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="" ${allChecked} class="tg-df-brand-chk"> Any Brand${_div}`;                    values.forEach(v => {             if (!v.formatted_value || v.formatted_value === 'Any Brand') return;             const isChecked = this.selectedBrands.includes(v.formatted_value) ? 'checked' : '';             html += `\x3Cdiv class="tg-df-ms-option">\x3Cinput type="checkbox" value="${this.escapeHTML(v.formatted_value)}" ${isChecked} class="tg-df-brand-chk"> ${this.escapeHTML(v.formatted_value)} \x3Cspan style="color:var(--tg-df-text-muted);font-size:12px">(${v.count || 0})${_span}${_div}`;          });                    this.brandDropdown.innerHTML = html;                    // Re-bind listeners          const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');          chks.forEach(chk => {            chk.addEventListener('change', (e) => {              const val = e.target.value;              if (val === '') {                this.selectedBrands = [];              } else {                if (e.target.checked) {                   if (!this.selectedBrands.includes(val)) this.selectedBrands.push(val);                } else {                   this.selectedBrands = this.selectedBrands.filter(b => b !== val);                }              }                            if (this.selectedBrands.length === 0) {                 this.brandTrigger.innerText = 'Any Brand';              } else if (this.selectedBrands.length === 1) {                 this.brandTrigger.innerText = this.selectedBrands[0];              } else {                 this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;              }                            // Only call API if changed from UI interactions              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.updateURLParams();                 this.fetchDeals(this.currentQuery);              }            });          });                    // Update button text on load          if (this.selectedBrands.length === 0) {             this.brandTrigger.innerText = 'Any Brand';          } else if (this.selectedBrands.length === 1) {             this.brandTrigger.innerText = this.selectedBrands[0];          } else {             this.brandTrigger.innerText = `${this.selectedBrands.length} Brands selected`;          }        }        updateURLParams() {          const url = new URL(window.location);          if (this.currentQuery && this.currentQuery !== 'Gaming laptops') {            url.searchParams.set('q', this.currentQuery);          } else {            url.searchParams.delete('q');            url.searchParams.delete('search');            url.searchParams.delete('query');          }                    if (this.regionSelect && this.regionSelect.value !== 'auto') {            url.searchParams.set('region', this.regionSelect.value);          } else {            url.searchParams.delete('region');          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.set('retailer', this.retailerSelect.value);          } else {            url.searchParams.delete('retailer');          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.set('brands', this.selectedBrands.join(','));          } else {            url.searchParams.delete('brands');          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.set('offer_type', this.offerTypeSelect.value);          } else {            url.searchParams.delete('offer_type');          }                    if (this.viewModeSelect && this.viewModeSelect.value !== 'auto') {            url.searchParams.set('view_mode', this.viewModeSelect.value);          } else {            url.searchParams.delete('view_mode');          }                    if (this.rowsSelect && this.rowsSelect.value !== '12') {            url.searchParams.set('rows', this.rowsSelect.value);          } else {            url.searchParams.delete('rows');          }                    const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             url.searchParams.set('price', `${min}_${max}`);          } else if (this.priceFilter && this.priceFilter.value !== 'all') {            url.searchParams.set('price', this.priceFilter.value);          } else {            url.searchParams.delete('price');          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {               const ratio = (100 - v) / 100;               url.searchParams.set('min_discount_ratio', ratio.toString());            }          } else {            url.searchParams.delete('min_discount_ratio');          }                    if (this.sortSelect && this.sortSelect.value !== 'discount_desc') {            url.searchParams.set('sort', this.sortSelect.value);          } else {            url.searchParams.delete('sort');          }                    if (this.dealModeToggle && this.dealModeToggle.checked) {            url.searchParams.set('deal_mode', 'true');          } else {            url.searchParams.delete('deal_mode');          }                    window.history.replaceState({}, '', url);        }        bindEvents() {          const roundels = this.root.querySelectorAll('.tg-df-carousel-cat.original-hardcoded');          roundels.forEach(r => {             r.addEventListener('click', () => {                const q = r.getAttribute('data-query');                const pr = r.getAttribute('data-pr');                if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: q,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = q;                const label = this.root.querySelector('#tg-df-carousel-title-label');                if (label) label.textContent = 'Best ' + q;                if (this.priceFilter) this.priceFilter.value = pr || 'all';                if (this.discountFilter) this.discountFilter.value = '5';                if (this.searchInput) this.searchInput.value = q;                                roundels.forEach(ro => ro.classList.remove('active'));                r.classList.add('active');                this.fetchDeals(this.currentQuery);             });          });          const discBtns = this.root.querySelectorAll('.tg-df-carousel-filter-btn');          discBtns.forEach(b => {             b.addEventListener('click', () => {                const d = b.getAttribute('data-d');                const pr = b.getAttribute('data-pr');                const ot = b.getAttribute('data-ot');                let label = b.innerText ? b.innerText.trim() : '';                let filterType = 'unknown';                let filterVal = 'unknown';                if (d !== null) { filterType = 'discount'; filterVal = d; }                else if (pr !== null) { filterType = 'price'; filterVal = pr; }                else if (ot !== null) { filterType = 'offertype'; filterVal = ot; }                if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-${filterType}-${filterVal}`, name: 'Filter Button', label: label });                                if (d !== null) {                   if (this.discountFilter) this.discountFilter.value = this.discountFilter.value === d ? '0' : d;                } else if (pr !== null) {                   if (this.priceFilter) this.priceFilter.value = this.priceFilter.value === pr ? 'all' : pr;                } else if (ot !== null) {                   if (this.offerTypeSelect) this.offerTypeSelect.value = this.offerTypeSelect.value === ot ? 'all' : ot;                } else {                   if (this.discountFilter) this.discountFilter.value = '0';                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.offerTypeSelect) this.offerTypeSelect.value = 'all';                }                if (d === null && pr === null && ot === null && b.getAttribute("data-type") !== "custom") {                   discBtns.forEach(ro => ro.classList.remove('active'));                   b.classList.add('active');                } else if (b.getAttribute("data-type") !== "custom") {                   // Only operate on hardcoded buttons (those without data-type)                   discBtns.forEach(ro => {                      if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active');                   });                                      let makeActive = true;                   if (d !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-d') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (pr !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-pr') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   } else if (ot !== null) {                       if (b.classList.contains('active')) makeActive = false;                       discBtns.forEach(ro => { if (ro.getAttribute('data-ot') !== null && ro.getAttribute('data-type') !== 'custom') ro.classList.remove('active') });                   }                                      if (makeActive) b.classList.add('active');                                      // Check if anything is active, if not activate "All"                   let anyActive = false;                   discBtns.forEach(ro => { if (ro.classList.contains('active') && ro.getAttribute('data-type') !== 'custom') anyActive = true; });                   if (!anyActive) {                       discBtns.forEach(ro => { if (!ro.getAttribute('data-d') && !ro.getAttribute('data-pr') && !ro.getAttribute('data-ot') && ro.getAttribute('data-type') !== 'custom') ro.classList.add('active'); });                   }                }                                this.fetchDeals(this.currentQuery);             });          });          if (this.brandTrigger && this.brandDropdown) {            this.brandTrigger.addEventListener('click', () => {              this.brandDropdown.classList.toggle('active');            });            document.addEventListener('click', (e) => {              if (this.brandFilterWrapper && !e.composedPath().includes(this.brandFilterWrapper)) {                this.brandDropdown.classList.remove('active');              }            });          }          let debounceTimer;          if(this.searchInput) {            this.searchInput.addEventListener('input', (e) => {              clearTimeout(debounceTimer);              const query = e.target.value.trim();              this.currentQuery = query;              if (this.getViewMode() === 'savings_squad' && this.autocompleteDropdown && this.airedaleTags && query.length > 0) {                 const matches = this.airedaleTags.filter(t => t.toLowerCase().includes(query.toLowerCase()) && t.toLowerCase() !== query.toLowerCase()).slice(0, 5);                 if (matches.length > 0) {                    this.autocompleteDropdown.innerHTML = matches.map(m => `\x3Cdiv class="tg-df-autocomplete-item" data-tag="${this.escapeHTML(m)}">${this.escapeHTML(m)}<` + `/div>`).join('');                    this.autocompleteDropdown.classList.add('active');                 } else {                    this.autocompleteDropdown.classList.remove('active');                 }              } else if (this.autocompleteDropdown) {                 this.autocompleteDropdown.classList.remove('active');              }              debounceTimer = setTimeout(() => {                this.updateURLParams();                if (query.length > 2) {                  this.fetchDeals(query);                } else if (query.length === 0) {                  this.deals = [];                  this.render();                }              }, 400);            });            this.searchInput.addEventListener('keypress', (e) => {              if (e.key === 'Enter') {                if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');                clearTimeout(debounceTimer);                const query = e.target.value.trim();                this.currentQuery = query;                this.activeDealTag = null;                trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });                this.updateURLParams();                if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                   this.fetchDeals(query);                }              }            });          }          if (this.autocompleteDropdown) {             this.autocompleteDropdown.addEventListener('click', (e) => {                const item = e.target.closest('.tg-df-autocomplete-item');                if (item) {                   const tag = item.getAttribute('data-tag');                   this.currentQuery = tag;                   if (this.searchInput) this.searchInput.value = tag;                   this.activeDealTag = tag;                   this.autocompleteDropdown.classList.remove('active');                   this.updateURLParams();                   this.fetchDeals(tag);                }             });             document.addEventListener('click', (e) => {               if (this.autocompleteDropdown && this.searchInput && !e.composedPath().includes(this.searchInput) && !e.composedPath().includes(this.autocompleteDropdown)) {                 this.autocompleteDropdown.classList.remove('active');               }             });          }          if (this.searchBtn) {            this.searchBtn.addEventListener('click', () => {              if (this.autocompleteDropdown) this.autocompleteDropdown.classList.remove('active');              clearTimeout(debounceTimer);              const query = this.searchInput.value.trim();              trackElementInteraction({ id: 'search-submit', name: 'Ask', label: 'Ask (main search)', text: query });              this.activeDealTag = null;              this.currentQuery = query;              this.updateURLParams();              if (query.length > 2 || (this.getViewMode() === 'savings_squad')) {                 this.fetchDeals(query);              }            });          }          if(this.sortSelect) this.sortSelect.addEventListener('change', () => {            trackElementInteraction({ id: `sort-option-${this.sortSelect.value}`, name: 'Sort', label: `Sort: ${this.sortSelect.options[this.sortSelect.selectedIndex].text}` });            this.updateURLParams();            if (this.deals.length > 0) {              this.sortData();              this.render();            }          });                    const priceFilter = this.root.querySelector('#tg-df-price-filter');          if (priceFilter) {            this.priceFilter = priceFilter;            this.priceFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-price-${this.priceFilter.value}`, name: 'Price', label: this.priceFilter.options[this.priceFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          const updateCustomPrice = () => {             this.updateURLParams();             if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);             } else {                this.render();             }          };          if (this.customPriceMin) {             this.customPriceMin.addEventListener('change', updateCustomPrice);             this.customPriceMin.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          if (this.customPriceMax) {             this.customPriceMax.addEventListener('change', updateCustomPrice);             this.customPriceMax.addEventListener('keypress', (e) => {                if (e.key === 'Enter') updateCustomPrice();             });          }          const discountFilter = this.root.querySelector('#tg-df-discount-filter');          if (discountFilter) {            this.discountFilter = discountFilter;            this.discountFilter.addEventListener('change', () => {              trackElementInteraction({ id: `filter-discount-${this.discountFilter.value}`, name: 'Discount', label: this.discountFilter.options[this.discountFilter.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }            });          }          if (this.categoryFilter) {            this.categoryFilter.addEventListener('change', (e) => {               const val = e.target.value === 'all' ? null : e.target.value;               this.activeDealTag = val;               this.fetchSavingsSquad();            });          }                    if (this.settingsToggle) {            this.settingsToggle.addEventListener('click', () => {              const o = this.settingsPanel.classList.toggle('active');              this.settingsBackdrop.classList.toggle('active');              if (o) trackElementInteraction({ id: 'filter-open', name: 'Filters', label: 'Open filters' });            });          }                    if (this.settingsBackdrop) {            this.settingsBackdrop.addEventListener('click', () => {              this.settingsPanel.classList.remove('active');              this.settingsBackdrop.classList.remove('active');            });          }                    if (this.regionSelect) {            this.regionSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-region-${this.regionSelect.value}`, name: 'Region', label: this.regionSelect.options[this.regionSelect.selectedIndex].text });              this.updateURLParams();              this.updatePriceDropdownCurrency();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.retailerSelect) {            this.retailerSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-merchant-${this.retailerSelect.value}`, name: 'Retailer', label: this.retailerSelect.options[this.retailerSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.offerTypeSelect) {            this.offerTypeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-offertype-${this.offerTypeSelect.value}`, name: 'Offer Type', label: this.offerTypeSelect.options[this.offerTypeSelect.selectedIndex].text });              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.viewModeSelect) {            this._prevViewMode = this.viewModeSelect.value;            this.viewModeSelect.addEventListener('change', () => {              trackElementInteraction({ id: `filter-viewmode-${this.viewModeSelect.value}`, name: 'View Mode', label: this.viewModeSelect.options[this.viewModeSelect.selectedIndex].text });                            // Reset all active toggles and filters to prevent config carry-over              this.selectedBrands = [];              if (this.brandTrigger) this.brandTrigger.innerText = 'Select Brands';              if (this.brandDropdown) {                const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                chks.forEach(chk => { chk.checked = false; });              }              if (this.priceFilter) this.priceFilter.value = 'all';              if (this.customPriceMin) this.customPriceMin.value = '';              if (this.customPriceMax) this.customPriceMax.value = '';              if (this.sortSelect) this.sortSelect.value = this.viewModeSelect.value === 'savings_squad' ? 'date_desc' : 'discount_desc';              if (this.discountFilter) this.discountFilter.value = '0';              if (this.retailerSelect) this.retailerSelect.value = '';              if (this.offerTypeSelect) this.offerTypeSelect.value = '';              if (this.rowsSelect) this.rowsSelect.value = '12';              if (this.categoryFilter) this.categoryFilter.value = 'all';              this.activeDealTag = null;              this.updateURLParams();              this.applyLayoutMode();                            if (this.getViewMode() === 'savings_squad' || this._prevViewMode === 'savings_squad') {                this.fetchDeals(this.currentQuery);              } else {                this.render();              }              this._prevViewMode = this.viewModeSelect.value;            });          }                    if (this.rowsSelect) {            this.rowsSelect.addEventListener('change', () => {              this.updateURLParams();              if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {                this.fetchDeals(this.currentQuery);              }            });          }                    if (this.dealModeToggle) {            this.dealModeToggle.addEventListener('change', () => {              this.updateURLParams();              this.render();            });          }          if (this.editorModeToggle) {             this.editorModeToggle.addEventListener('change', (e) => {                this.editorMode = e.target.checked;                this.render();                this.updateFloatingCopyBar();             });          }          if (this.editorCopyBtn) {             this.editorCopyBtn.addEventListener('click', () => {                this.copySelectedDealsToCMS();             });          }          if (this.editorClearBtn) {             this.editorClearBtn.addEventListener('click', () => {                this.selectedDeals.clear();                this.render();                this.updateFloatingCopyBar();             });          }          if (this.grid) {            this.grid.addEventListener('change', (e) => {               if (e.target.classList.contains('tg-df-deal-checkbox')) {                  const dealId = e.target.getAttribute('data-id');                  if (e.target.checked) {                     const dealObj = this.deals.find(d => d.id === dealId);                     if (dealObj) this.selectedDeals.set(dealId, dealObj);                  } else {                     this.selectedDeals.delete(dealId);                  }                  this.updateFloatingCopyBar();               }            });            this.grid.addEventListener('click', (e) => {              const dealCard = e.target.closest('[data-action="deal-click"]');              const similarCard = e.target.closest('[data-action="view-similar-click"]');              const cardLink = dealCard || similarCard;              if (cardLink) {                const productName = cardLink.getAttribute('data-product-name');                const merchantName = cardLink.getAttribute('data-merchant-name');                const productId = cardLink.getAttribute('data-analytics-id');                const price = parseFloat(cardLink.getAttribute('data-price')) || null;                const prevPriceStr = cardLink.getAttribute('data-previous-price');                const previousPrice = prevPriceStr ? parseFloat(prevPriceStr) : null;                const originalLink = cardLink.getAttribute('data-original-link');                const rewrittenLink = cardLink.getAttribute('href');                const revenueId = cardLink.getAttribute('data-revenue-id');                const index = parseInt(cardLink.getAttribute('data-index'), 10) || 0;                const inStock = cardLink.getAttribute('data-in-stock') === 'true';                const totalText = cardLink.getAttribute('data-total');                const totalDeals = parseInt(totalText, 10) || 0;                const productCategoryName = 'deals';                const trackingParams = {                  widgetId: this.widgetId,                  productCategoryName: productCategoryName,                  product: {                    modelId: cardLink.getAttribute('data-model-id') || null,                    matchId: cardLink.getAttribute('data-match-id') || null,                    brand: cardLink.getAttribute('data-model-brand') || null,                    parent: cardLink.getAttribute('data-model-parent') || null,                    name: productName,                    price: price,                    previousPrice: previousPrice,                    link: rewrittenLink,                    originalLink: originalLink,                    inStock: inStock                  },                  zeroBasedProductIndexOrNull: index,                  totalDealsOrProducts: totalDeals,                   merchant: {                    id: cardLink.getAttribute('data-merchant-id') || null,                    network: cardLink.getAttribute('data-merchant-network') || null,                    url: cardLink.getAttribute('data-merchant-url') || null,                    name: merchantName                  },                  revenueId: revenueId,                  widgetTypeName: this.widgetTypeName,                  isoCurrencyCode: normalizeCurrency(this.escapeHTML(cardLink.getAttribute('data-currency') || '$'))                };                if (dealCard) {                  trackDealClick(trackingParams);                } else {                  trackViewSimilarClick(trackingParams);                }              }              const couponsBtn = e.target.closest('[data-action="coupons-click"]');              if (couponsBtn) {                trackElementInteraction({                  id: 'product-card-show-coupons',                  name: 'Coupons',                  label: `Product card coupons: ${couponsBtn.getAttribute('data-merchant')}`                });              }            });          }        }        get widgetTypeName() {          const mode = this.viewModeSelect ? this.viewModeSelect.value : (this.viewModeOverride || 'auto');          switch(mode) {              case 'carousel': return 'Carousel';              case 'savings_squad': return 'Savings Squad';              case 'grid': return 'Grid';              case 'row': return 'Row';              default: return 'Auto Collection';          }        }        getAreaCode() {          if (this.regionSelect && this.regionSelect.value) {            if (this.regionSelect.value === 'auto') return null;            return this.regionSelect.value;          }          let area = null;          try {            const locale = window.navigator.language || window.navigator.userLanguage;            if (locale && locale.includes('-')) {              area = locale.split('-')[1].toUpperCase();            } else if (locale && locale.length === 2) {              if (locale.toUpperCase() === 'EN') { area = 'US'; }              else { area = locale.toUpperCase(); }            }          } catch (e) { /* Ignore */ }                    // Map to known valid options or fallback to US          const valid = ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'IT'];          if (area === 'UK') area = 'GB';          if (valid.includes(area)) {             return area;          }          return 'US';        }                async loadCarouselSpreadsheet() {          try {              const parseCSVRow = (str) => {                  let result = [], cur = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (inQuotes) {                          if (char === '"') {                              if (str[i + 1] === '"') { cur += '"'; i++; }                              else { inQuotes = false; }                          } else { cur += char; }                      } else {                          if (char === '"') { inQuotes = true; }                          else if (char === ',') { result.push(cur); cur = ''; }                          else { cur += char; }                      }                  }                  result.push(cur); return result;              };              const parseCSV = (str) => {                  const rows = []; let curRow = '', inQuotes = false;                  for (let i = 0; i < str.length; i++) {                      let char = str[i];                      if (char === '"') inQuotes = !inQuotes;                      if ((char === '\n' || char === '\r') && !inQuotes) {                          if (char === '\r' && str[i+1] === '\n') i++;                          if (curRow) rows.push(parseCSVRow(curRow));                          curRow = '';                      } else { curRow += char; }                  }                  if (curRow) rows.push(parseCSVRow(curRow));                  return rows;              };              const preloadedCSV = decodeURIComponent(escape(atob("LCwxLDIsMyw0LDUsNiw3LDgsOSwxMCwxMSwxMiwxMywxNCwxNQ0KUm91bmRlbCB0ZXh0LEFsbCxUVnMsRm9vdHdlYXIsQXBwYXJlbCxNYXR0cmVzZXMsQXBwbGlhbmNlcyxXZWFyYWJsZSB0ZWNoLEhlYWRwaG9uZXMsU21hcnQgSG9tZSxTcGVha2VycyxMYXB0b3BzLFRhYmxldHMsQ29tcHV0aW5nLFBob25lcyxHYW1pbmcsTGVnbw0KUm91bmRlbCBpbWFnZSxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2FpLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3R2cy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvN2IzYTIyNGIwNzk2M2M2MjdiNmI5MDliZDc4MzM4MzZlMDJmZjgxOS5qcGcud2VicCxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy84NGRhYzVkNDhlZDJkNDQ4NTU5ZWJhNjdhY2U4MzE0Y2M2N2NjZDk0LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvbWF0dHJlc3Nlcy5wbmcsaHR0cHM6Ly9pbWFnZXMuZmllLmZ1dHVyZWNkbi5uZXQvcHJvZHVjdHMvNzY4ZTk3Y2ViMDcxODAxZmFlMjA5MTBkMDgyMGIxNmY3NDdhZjkzOS5qcGcud2VicCxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3dlbGxuZXNzLnBuZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL2hlYWRwaG9uZXMuanBnLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzg5NTM1YmVlYmUyMGRiYmQ0YTM0NmQ2ZDZiZGZlOTFkOGE4ODRhMjEuanBnLndlYnAsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9hdWRpby5qcGcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9sYXB0b3BzLmpwZyxodHRwczovL2ltYWdlcy5maWUuZnV0dXJlY2RuLm5ldC9wcm9kdWN0cy8yMzk3NTY0ZWQ3YTVmZjk0N2U5YjZiMzBlNTRmNDc0OTRiODQxZjg5LmpwZy53ZWJwLGh0dHBzOi8vd3d3LnRvbXNndWlkZS5jb20vcHJvZHVjdHMvY2Fyb3VzZWwvY29tcHV0aW5nLmpwZyxodHRwczovL3d3dy50b21zZ3VpZGUuY29tL3Byb2R1Y3RzL2Nhcm91c2VsL3Bob25lcy5wbmcsaHR0cHM6Ly93d3cudG9tc2d1aWRlLmNvbS9wcm9kdWN0cy9jYXJvdXNlbC9nYW1pbmcucG5nLGh0dHBzOi8vaW1hZ2VzLmZpZS5mdXR1cmVjZG4ubmV0L3Byb2R1Y3RzLzRmNmM2MjFjYWMwYmMxYTg1ZDU5M2UzNTk0YmE1YjM0OWVmZmQyOTIuanBnLndlYnANClNlYXJjaCBRdWVyeSxFdmVyeXRoaW5nLFRlbGV2aXNpb25zLCJTbmVha2VycywgcnVubmluZyBzaG9lcywgc2FuZGFscyIsQ2xvdGhpbmcsTWF0dHJlc3NlcyxIb21lIEFwcGxpYW5jZXMsV2VhcmFibGVzICYgRml0bmVzcyBUZWNoLEhlYWRwaG9uZXMsSG9tZSBUZWNoLFNwZWFrZXJzLExhcHRvcHMsVGFibGV0cyxDb21wdXRpbmcsUGhvbmVzLEdhbWluZyxDb25zdHJ1Y3Rpb24gVG95cw0KRGlzY291bnQgQW1vdW50LG1pbiA1JSxtaW4gMTAlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUsbWluIDUlLG1pbiA1JSxtaW4gNSUNClByaWNlIFJhbmdlLCwsLCxtaW4gJDQwMCwsLCxtaW4gJDI1LCxtaW4gJDMwMCwsLG1pbiAkMTAwLCwNCkJyYW5kIFNlbGVjdGlvbiwsLCwsLCwsLCwsLCwsLCwNCkZpbHRlciBidXR0b25zLCwsLCwsLCwsLCwsLCwsLA0KMSxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMsTGlnaHRuaW5nIGRlYWxzLExpZ2h0bmluZyBkZWFscyxMaWdodG5pbmcgZGVhbHMNCjIsQW1hem9uIGRlYWxzLFVuZGVyICQxMDAwLDUwJSBvZmYsQWRpZGFzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsNTAlIG9mZixBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscyxBbWF6b24gZGVhbHMsQW1hem9uIGRlYWxzLEFtYXpvbiBkZWFscw0KMyxPdmVyICQ0MDAsVW5kZXIgJDUwMCxIb2thLE5pa2UsU2FhdHZhLE5pbmphLDQwJSBvZmYsSkxhYiwsSkJMLERlbGwsLEFzdXMsQXBwbGUsQ29uc29sZXMsU3RhciBXYXJzDQo0LFVuZGVyICQxMDAwLDUwJSBvZmYsU2tlY2hlcnMsVW5kZXIgQXJtb3VyLEhlbGl4LFNoYXJrLEdhcm1pbixBbmtlciBTb3VuZGNvcmUsUmluZyxTb25vcyxBcHBsZSxBcHBsZSxUUC1saW5rLFNhbXN1bmcsQWNjZXNzb3JpZXMsVW5kZXIgJDI1DQo1LFVuZGVyICQ1MDAsTEcsQXNpY3MsQ29sdW1iaWEsRHJlYW1DbG91ZCxLZXVyaWcsQXBwbGUsU29ueSxHb3ZlZSxUcmliaXQsTGVub3ZvLFNhbXN1bmcsRWVybyxHb29nbGUsR2FtZXMsVW5kZXIgJDUwDQo2LDUwJSBvZmYsU2Ftc3VuZyxOaWtlLFBhdGFnb25pYSxOZWN0YXIsRGUnTG9uZ2hpLEFtYXpmaXQsQXBwbGUsS2FzYSBzbWFydCxTb255LEFsaWVud2FyZSxUQ0wsTmV0Z2VhcixNb3Rvcm9sYSxOaW50ZW5kbyxCb3RhbmljYWxzDQo3LEFtYXpvbixIaXNlbnNlLE5ldyBCYWxhbmNlLEFyYyd0ZXJ5eCxUZW1wdXItcGVkaWMsRHlzb24sRml0Yml0LEJlYXRzLFBoaWxpcHMgSHVlLEFua2VyLEFjZXIsT25lUGx1cyxEZWxsLE9uZVBsdXMsU29ueSxEaXNuZXkNCjgsQXBwbGUsVENMLEFkaWRhcyxDYXJoYXJ0dCxCZWFyLEJpc3NlbGwsU2Ftc3VuZyxFYXJmdW4sQmxpbmssQmVhdHMsTVNJLE1pY3Jvc29mdCxBY2VyLE5vdGhpbmcsWGJveCxNYXJ2ZWwNCjksLFNvbnksU2F1Y29ueSxUaGUgTm9ydGggRmFjZSxTaWVuYSxOdXRyaWJ1bGxldCxPdXJhLFNhbXN1bmcsR29vZ2xlIE5lc3QgLE1hcnNoYWxsLFNhbXN1bmcsTGVub3ZvLExlbm92bywsLFBva2Vtb24NCjEwLCxSb2t1LEJpcmtlbnN0b2NrLENSWiBZb2dhLFdpbmtCZWRzLEJsYWNrIGFuZCBEZWNrZXIsUmluZ2Nvbm4sQ01GLEV1ZnksU2Ftc3VuZyxNaWNyb3NvZnQsUmVNYXJrYWJsZSxBbGllbndhcmUsLCwNCjExLCwsQnJvb2tzLFRoZSBHeW0gUGVvcGxlLEJyb29rbHluIGJlZGRpbmcsTmVzcHJlc3NvLCwxTW9yZSxBcmxvLCxSYXplciwsQ29yc2FpciwsLA0KMTIsLCxDcm9jcywsRWlnaHQgU2xlZXAsQ3Vpc2luYXJ0LCxKQkwsLCwsLEhQLCwsDQpOb3RlcywsLCwsLCwsLCwsLCwsLCwNCiwsIlByaW9yaXRpc2UgYmlnZ2VzdCAlLyQgZGlzY291bnQsIFR2cyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiB0aGUgbW9zdCBwb3B1bGFyIGV2ZW4gaWYgdGhleSBhcmUgc3RpbGwgZXhwZW5zaXZlIiwiTm8gcGF0dGVybiB0byBwcmljaW5nL2Rpc2NvdW50LCByZWFkZXJzIG1haW5seSBzaG9wIGJ5IGJyYW5kL3JlY29nbmlzYWJsZSBzaG9lcyIsIk5vIHBhdHRlcm4gdG8gcHJpY2luZy9kaXNjb3VudCwgcmVhZGVycyBtYWlubHkgc2hvcCBieSBicmFuZCIsIkEgbGFiZWwgd2lsbCBkZWZpbml0ZWx5IGhlbHAgaGVyZSBlLmcuIGJlc3QgZm9yIHNpZGUgc2xlZXBlciwgYmVzdCBtZW1vcnkgZm9hbSIsIkFwcGxpYW5jZXMgaXMgYSBiaWcgY2F0ZWdvcnksIGlzIGl0IHBvc3NpYmxlIHRvIHNwbGl0IGludG8ga2l0Y2hlbiBhcHBsaWFuY2VzLCBmbG9vcmNhcmUsIGFpciBoZWFsdGgvY29vbGluZz8gT3Igc2ltaWxhciIsIkZvY3VzIG9uIHZhbHVlIGZvciBtb25leSwgR2FybWlucyB3aXRoIH41MCUgb2ZmIGhhdmUgYmVlbiBwb3B1bGFyIGV2ZW4gdGhvdWdoIHRoZXkgYXJlIHN0aWxsICQ1MDAiLCwsLCxJbmNsdWRlIEtpbmRsZXMsSSB3b3VsZCBpbmNsdWRlIHdpZmkgcm91dGVycyBoZXJlIGluc3RlYWQgb2Ygc21hcnQgaG9tZSxDYW4gd2Ugc3VyZmFjZSBwaG9uZSBwcm92aWRlciBkZWFscz8gVC1tb2JpbGUgYW5kIHZlcml6b24gd291bGQgbWFrZSBhIGxvdCBtb3JlIG1vbmV5IHRoYW4gQW1hem9uLCwNCiwsaGF2aW5nIGEgJ2Jlc3QgZm9yJyBsYWJlbCB3b3VsZCBiZSBoZWxwZnVsIGUuZy4gYmVzdCBmb3IgYnJpZ2h0IHJvb20sQ2FuIHdlIHN0b3Aga2lkcyBzaG9lcyBmcm9tIHB1bGxpbmcgdGhyb3VnaD8sIldpbGwgdGhpcyBpbmNsdWRlIGFjY2Vzc29yaWVzIGUuZy4gY2FwcywgYmFncywgaWYgc28gbWFrZSBzdXJlIHRoZXNlIGFyZSBtaXhlZCB0aHJvdWdob3V0IGNsb3RoaW5nIGRlYWxzIixXaWxsIHRoaXMgaW5jbHVkZSB0b3BwZXJzIGFuZCBwaWxsb3dzPyBTZWVpbmcgbW9yZSBtb21lbnR1bSB3aXRoIHRoaXMgY2F0ZWdvcnkgcmVjZW50bHkgc28gYSBiZWRkaW5nIHRhYiBtaWdodCB3b3JrLCwiTmVlZCB0byBtYWtlIHN1cmUgYmFuZHMsIHNjcmVlbiBwcm90ZWN0b3JzIGV0Yy4gZG9uJ3QgcHVsbCBpbnRvIGhlcmUiLCwsLCwsLCwsDQosLCJQcmlvcml0aXNlIDY1JycgYW5kIDU1JyBpbmNoIFRWcywgdGhlbiBiaWdnZXIgc2NyZWVucyBiZWZvcmUgdGhlIHNtYWxsZXIgc2l6ZXMiLCwsUXVlZW4gaXMgdGhlIG1vc3QgcG9wdWxhciBzaXplIGluIHRoZSBVUyAtIHByaW9yaXRpc2UgZGVhbHMgZm9yIHRoaXMgc2l6ZSwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpDYXRlZ29yaWVzIHRvIGNvbnNpZGVyLCxQcm9kdWN0cyBpbmNsdWRlZCwsLCwsLCwsLCwsLCwsDQpVbmRlciAkNTA/LCxBaXIgdGFncywsLCwsLCwsLCwsLCwsDQosLFBvcnRhYmxlIGNoYXJnZXJzL3dpcmVsZXNzIGNoYXJnZXJzLCwsLCwsLCwsLCwsLCwNCiwsIldhdGVyIGJvdHRsZXMgKHN0YW5sZXlzLCBPd2FsYSwgSHlkcm8gZmxhc2ssIFlldGkpIiwsLCwsLCwsLCwsLCwsDQosLEhhbmQgaGVsZCBmYW5zLCwsLCwsLCwsLCwsLCwNCiwsLCwsLCwsLCwsLCwsLCwNCmhvbWUgb2ZmaWNlLCxvZmZpY2UgY2hhaXJzLCwsLCwsLCwsLCwsLCwNCiwsc3RhbmRpbmcgZGVza3MsLCwsLCwsLCwsLCwsLA0KLCxtb25pdG9ycywsLCwsLCwsLCwsLCwsDQosLEtleWJvYXJkcywsLCwsLCwsLCwsLCwsDQosLGRvY2tpbmcgc3RhdGlvbiwsLCwsLCwsLCwsLCwsDQosLCwsLCwsLCwsLCwsLCwsDQpHYW1pbmcsLENvbnNvbGVzLCwsLCwsLCwsLCwsLCwNCiwsQWNjZXNzb3JpZXMsLCwsLCwsLCwsLCwsLA0KLCxHYW1lcywsLCwsLCwsLCwsLCwsDQosLENvdWxkIGluY2x1ZGUgTGVnbz8sLCwsLCwsLCwsLCwsLA==")));              const text = preloadedCSV;              const parsed = parseCSV(text);                            const rowsByName = {};              let filterStart = -1;              parsed.forEach((rc, i) => {                 if (rc && rc.length > 0 && rc[0]) rowsByName[rc[0]] = rc;                 if (rc && rc.length > 0 && rc[0] === 'Filter buttons') filterStart = i;              });                            const cols = [];              if(rowsByName['Roundel text']) {                const headerRow = rowsByName['Roundel text'];                for(let col = 1; col < headerRow.length; col++) {                   let label = headerRow[col];                   if (!label) continue;                                      let q = rowsByName['Search Query'] && rowsByName['Search Query'][col] ? rowsByName['Search Query'][col] : '';                   let img = rowsByName['Roundel image'] && rowsByName['Roundel image'][col] ? rowsByName['Roundel image'][col] : '';                   let ds = rowsByName['Discount Amount'] && rowsByName['Discount Amount'][col] ? rowsByName['Discount Amount'][col] : '';                   let pr = rowsByName['Price Range'] && rowsByName['Price Range'][col] ? rowsByName['Price Range'][col] : '';                   let rt = rowsByName['Retailer'] && rowsByName['Retailer'][col] ? rowsByName['Retailer'][col] : '';                   let ot = rowsByName['Offer Type'] && rowsByName['Offer Type'][col] ? rowsByName['Offer Type'][col] : '';                                      let filters = [];                   if(filterStart > 0) {                     for(let r = filterStart + 1; r < parsed.length; r++) {                         if(!parsed[r] || parsed[r][0] === 'Notes' || parsed[r][0] === 'Categories to consider') break;                         let f = parsed[r][col];                         if(f) filters.push(f);                     }                   }                   cols.push({ label, img, q, ds, pr, rt, ot, filters });                }              }              this.carouselData = cols;              if (this.carouselData && this.carouselData.length > 0) {                 const isMatched = this.carouselData.some(c => c.q === this.currentQuery || c.label === this.currentQuery);                 if (!isMatched) {                    const first = this.carouselData[0];                    this.currentQuery = first.q || first.label;                    if (this.priceFilter) this.priceFilter.value = 'all';                    if (this.customPriceMin) this.customPriceMin.value = '';                    if (this.customPriceMax) this.customPriceMax.value = '';                    let dPr = first.pr || 'all';                    if (typeof dPr === 'string' && dPr !== 'all') {                       let prLower = dPr.toLowerCase();                       if (prLower.includes('min') || prLower.includes('over')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else if (prLower.includes('max') || prLower.includes('under')) {                          let m = dPr.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       }                    }                    let dAm = '0';                    if(first.ds && typeof first.ds === 'string') {                       let m = first.ds.match(/(\d+)/);                       if(m) dAm = m[1];                    }                    if (this.discountFilter) this.discountFilter.value = dAm;                    if (this.offerTypeSelect) this.offerTypeSelect.value = first.ot || '';                    if (this.retailerSelect) this.retailerSelect.value = first.rt || '';                    this.selectedBrands = [];                    if (this.brandDropdown) {                        const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                        chks.forEach(chk => chk.checked = false);                    }                    if (this.searchInput) this.searchInput.value = this.currentQuery;                 }              }              this.renderCarouselUI();          } catch(e){ console.warn(e); }        }                renderCarouselUI() {           const roundelWrapper = this.root.querySelector('.tg-df-carousel-roundels');           if(!roundelWrapper || !this.carouselData) return;                      let html = '';           this.carouselData.forEach(r => {              const q = r.q || r.label;              const isActive = (this.currentQuery === q || this.currentQuery === r.label) ? 'active' : '';              const imgHtml = r.img ? `\x3Cimg src="${r.img}" alt="${r.label}" />` : `\x3Csvg width="32" height="32" fill="#1F69FF" viewBox="0 0 24 24">\x3Cpath d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\x3C/svg>`;              html += `                \x3Cdiv class="tg-df-roundel tg-df-carousel-cat ${isActive}" data-label="${this.escapeHTML(r.label)}">                  \x3Cdiv class="tg-df-roundel-img-box">${imgHtml}\x3C/div>                  \x3Cspan class="tg-df-roundel-label">${this.escapeHTML(r.label)}\x3C/span>                \x3C/div>              `;           });           roundelWrapper.innerHTML = html;                      // Rebind clicks           const roundels = this.root.querySelectorAll('.tg-df-carousel-cat');           roundels.forEach(rNode => {             rNode.addEventListener('click', () => {                const r = this.carouselData.find(c => c.label === rNode.getAttribute('data-label'));                 if(!r) return;                                  if (typeof trackHawkEvent !== 'undefined') {                     trackHawkEvent({                         clickType: "CC",                         widgetId: this.widgetId,                         productCategoryName: "deals",                         zeroBasedProductIndexOrNull: null,                         totalDealsOrProducts: null,                         areaClicked: "Category Roundel",                         revenueId: this.revenueId,                         isoCurrencyCode: typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD',                         queryName: r.label,                         widgetTypeName: this.widgetTypeName                     });                 }                this.currentQuery = r.q || r.label;                const labelTitle = this.root.querySelector('#tg-df-carousel-title-label');                if (labelTitle) labelTitle.textContent = 'Best ' + this.currentQuery;                if (this.priceFilter) this.priceFilter.value = 'all';                if (this.customPriceMin) this.customPriceMin.value = '';                if (this.customPriceMax) this.customPriceMax.value = '';                let dPr = r.pr || 'all';                if (typeof dPr === 'string' && dPr !== 'all') {                   let prLower = dPr.toLowerCase();                   if (prLower.includes('min') || prLower.includes('over')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMin) this.customPriceMin.value = m[1];                   } else if (prLower.includes('max') || prLower.includes('under')) {                      let m = dPr.match(/(\d+)/);                      if (m && this.customPriceMax) this.customPriceMax.value = m[1];                   }                }                                let discountAmount = '0';                if(r.ds && typeof r.ds === 'string') {                   let m = r.ds.match(/(\d+)/);                   if(m) discountAmount = m[1];                }                if (this.discountFilter) this.discountFilter.value = discountAmount;                if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                                // Clear brands                    this.selectedBrands = [];                    if (this.brandDropdown) {                    const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                    chks.forEach(chk => chk.checked = false);                }                                if (this.searchInput) this.searchInput.value = this.currentQuery;                                roundels.forEach(ro => ro.classList.remove('active'));                if (rNode) rNode.classList.add('active');                                this.renderCarouselFilters(r);                this.fetchDeals(this.currentQuery);             });           });                      // Auto-highlight active           const activeR = this.carouselData.find(c => c.q === this.currentQuery || c.label === this.currentQuery);           if(activeR) this.renderCarouselFilters(activeR);        }                renderCarouselFilters(r) {           const filtersWrap = this.root.querySelector('.tg-df-carousel-filters-wrap');           if(!filtersWrap) return;                      let html = `\x3Cbutton class="tg-df-carousel-filter-btn" data-type="all">All\x3C/button>`;                      r.filters.forEach(f => {              let fL = f.toLowerCase();              let icon = '';              let logic = `data-type="custom" data-v="${this.escapeHTML(f)}"`;              if (fL === 'amazon deals' || fL === 'prime deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/fwoVXvL79turN3Ph535m38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/u75QjVpt3w2EsMimJiRo38-600-100.png" class="active-img" alt="" /> Prime deals\x3C/button>`;              } else if (fL === 'lightning deals') {                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>\x3Cimg src="https://cdn.mos.cms.futurecdn.net/HqAui7w97ft2NPqBtQ5r38-600-100.png" class="inactive-img" alt="" />\x3Cimg src="https://cdn.mos.cms.futurecdn.net/yWPQ5yyQRhUwVKzGwYbh38-600-100.png" class="active-img" alt="" /> Lightning deals\x3C/button>`;              } else {                 if (fL.includes('lightning')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap">\x3Cpolygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2">\x3C/polygon>\x3C/svg>`;                 } else if (fL.includes('% off')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tag">\x3Cpath d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z">\x3C/path>\x3Ccircle cx="7.5" cy="7.5" r=".5" fill="currentColor">\x3C/circle>\x3C/svg>`;                 } else if (fL.includes('under') || fL.includes('min ')) {                    icon = `\x3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-dollar-sign">\x3Cpath d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z">\x3C/path>\x3Cpath d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8">\x3C/path>\x3Cpath d="M12 18V6">\x3C/path>\x3C/svg>`;                 }                 html += `\x3Cbutton class="tg-df-carousel-filter-btn" ${logic}>${icon} ${this.escapeHTML(f)}\x3C/button>`;              }           });                      filtersWrap.innerHTML = html;                      const btns = filtersWrap.querySelectorAll('button');           btns.forEach(b => {             b.addEventListener('click', () => {                const type = b.getAttribute('data-type');                if (type === 'custom') {                   const v = b.getAttribute('data-v');                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: `filter-custom-${(v||'').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`, name: 'Custom Filter', label: v });                }                if (type === 'all') {                   if (typeof trackElementInteraction === 'function') trackElementInteraction({ id: 'filter-clear-all', name: 'Clear all', label: 'Clear all filters' });                   // reset everything                   btns.forEach(btn => btn.classList.remove('active'));                   b.classList.add('active');                                      // Reset prices                   if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   if (this.brandDropdown) {                     const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                     chks.forEach(chk => chk.checked = false);                   }                } else {                   const v = b.getAttribute('data-v');                   const fL = v.toLowerCase();                                      let mapRet = ['amazon', 'walmart', 'best buy', 'target', 'john lewis', 'currys', 'argos'];                   const getCategory = (s) => {                      if (s === 'lightning deals' || s === 'amazon deals' || s === 'prime deals') return 'offer';                      if (s.includes('% off')) return 'discount';                      if (s.includes('under') || s.includes('over') || s.includes('min') || s.includes('max')) return 'price';                      if (mapRet.includes(s)) return 'retailer';                      return 'brand';                   };                   const cat = getCategory(fL);                   const wasActive = b.classList.contains('active');                   if (cat !== 'brand') {                      btns.forEach(btn => {                          if (btn === b) return;                          if (btn.getAttribute('data-type') === 'all') return;                          const bV = btn.getAttribute('data-v');                          if (!bV) return;                          if (getCategory(bV.toLowerCase()) === cat) btn.classList.remove('active');                      });                   }                   if (wasActive) b.classList.remove('active');                   else b.classList.add('active');                   let anyActive = Array.from(btns).some(btn => btn !== btns[0] && btn.classList.contains('active'));                   if (!anyActive) {                       btns[0].click();                       return;                   } else {                       btns[0].classList.remove('active');                   }                                      if (this.priceFilter) this.priceFilter.value = 'all';                   if (this.customPriceMin) this.customPriceMin.value = '';                   if (this.customPriceMax) this.customPriceMax.value = '';                   let dPr = r.pr || 'all';                   if (typeof dPr === 'string' && dPr !== 'all') {                      let prLower = dPr.toLowerCase();                      if (prLower.includes('min') || prLower.includes('over')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMin) this.customPriceMin.value = m[1];                      } else if (prLower.includes('max') || prLower.includes('under')) {                         let m = dPr.match(/(\d+)/);                         if (m && this.customPriceMax) this.customPriceMax.value = m[1];                      }                   }                                      let discountAmount = '0';                   if(r.ds && typeof r.ds === 'string') {                      let m = r.ds.match(/(\d+)/);                      if(m) discountAmount = m[1];                   }                   if (this.discountFilter) this.discountFilter.value = discountAmount;                   if (this.offerTypeSelect) this.offerTypeSelect.value = r.ot || '';                   if (this.retailerSelect) this.retailerSelect.value = r.rt || '';                   this.selectedBrands = [];                   btns.forEach(btn => {                       if (!btn.classList.contains('active') || btn.getAttribute('data-type') === 'all') return;                       const vv = btn.getAttribute('data-v');                       const vl = vv.toLowerCase();                                              if (vl === 'lightning deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_lightning';                       } else if (vl === 'amazon deals' || vl === 'prime deals') {                          if (this.offerTypeSelect) this.offerTypeSelect.value = 'amazon_prime';                       } else if (vl.includes('% off')) {                          let m = vl.match(/(\d+)%/);                          if (m && this.discountFilter) this.discountFilter.value = m[1];                       } else if (vl.includes('under') || vl.includes('max')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMax) this.customPriceMax.value = m[1];                       } else if (vl.includes('min') || vl.includes('over')) {                          let m = vl.match(/(\d+)/);                          if (m && this.customPriceMin) this.customPriceMin.value = m[1];                       } else {                          let foundR = mapRet.find(x => x === vl);                          if (foundR) {                             let realR = ['Amazon', 'Walmart', 'Best Buy', 'Target', 'John Lewis', 'Currys', 'Argos'].find(x => x.toLowerCase() === vl);                             if (this.retailerSelect) this.retailerSelect.value = realR;                          } else {                             this.selectedBrands.push(vv);                          }                       }                   });                                      if (this.brandDropdown) {                       const chks = this.brandDropdown.querySelectorAll('.tg-df-brand-chk');                       chks.forEach(c => c.checked = this.selectedBrands.includes(c.value));                   }                                      if (r.pr && typeof r.pr === 'string') {                       let prL = r.pr.toLowerCase();                       if (prL.includes('under $')) {                           let m = prL.match(/under \$(\d+)/i);                           if (m && this.customPriceMax && !this.customPriceMax.value) this.customPriceMax.value = m[1];                       }                   }                }                                this.fetchDeals(this.currentQuery);             });           });                      // default to highlighting first           btns[0].classList.add('active');        }async fetchDeals(query, append = false) {          if (!append) {             this.showLoading();             this.deals = [];             this.displayLimit = (this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;          } else {             this.displayLimit += (this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12;          }                    try {            if (this.getViewMode() === 'savings_squad') {               await this.fetchSavingsSquad(append);            } else {               if (this.isBroadQuery(query)) {                 await this.fetchAdviserDeals(query, append);               } else {                 await this.fetchHawkDeals(query, append);                 if (this.deals.length === 0) {                   await this.fetchAdviserDeals(query, append);                 }               }            }          } catch (error) {            console.warn("[Tom's Guide Widget] Fetch error:", error);            this.showError();          }        }        async fetchSavingsSquad() {          let topArticles = this.airedaleArticles;          if (!topArticles) {            const airedaleUrl = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`;            let res;            try {               res = await fetch(airedaleUrl);            } catch(e) {               try { res = await fetch(`https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50`); } catch (err) { console.warn("Fallback fetch failed", err); return; }            }            if (!res.ok) throw new Error('Airedale API Error');            const articles = await res.json();            topArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);            this.airedaleArticles = topArticles;                        let tagCounts = {};            topArticles.forEach((a) => {              let articleTags = new Set();              if (a.articlecategory && Array.isArray(a.articlecategory)) {                 a.articlecategory.forEach((t) => articleTags.add(t));              }              articleTags.forEach(t => {                 tagCounts[t] = (tagCounts[t] || 0) + 1;              });            });                        this.airedaleTags = Object.keys(tagCounts).sort((a, b) => tagCounts[b] - tagCounts[a]);            this.airedaleTagCounts = tagCounts;          }                    let targetArticles = topArticles;          if (this.activeDealTag) {             const encodedTag = encodeURIComponent(this.activeDealTag.toLowerCase().replace(/\s+/g, '-'));             const url = `https://airedale.futurecdn.net/feeds/feed_1776420579726.json?site=tomsguide&articleType=deals&limit=50&articleCategoryHandle=${encodedTag}`;             try {                const res = await fetch(url);                if (res.ok) {                   const articles = await res.json();                   targetArticles = Array.isArray(articles) ? articles.slice(0, 50) : ((articles.data && Array.isArray(articles.data)) ? articles.data.slice(0, 50) : []);                }             } catch(e) {                console.warn("Failed to fetch by activeDealTag", e);             }          }          let extractedDeals = [];          let dynamicBrandsCounts = {};                    targetArticles.forEach((article) => {             if (!article.articlepage) return;                          let pageData = [];             try {                pageData = JSON.parse(article.articlepage[0]);             } catch(e){ console.warn(e); }                          const savingsSquad = pageData.filter((p) => p.type === 'deal' || p.type === 'featured-product');                          savingsSquad.forEach((block, idx) => {                const data = block.data || {};                const isFeatured = block.type === 'featured-product';                                const link = data.link || {};                const priceObj = data.price || {};                const image = data.image || {};                                if (data.brand) {                   data.brand = data.brand.replace(/^\d+\.\s*/, '').trim();                   dynamicBrandsCounts[data.brand] = (dynamicBrandsCounts[data.brand] || 0) + 1;                }                const externalUrl = isFeatured ? data.url : (link.href || null);                let summaryTitle = isFeatured ? (data.name || data.brand) : (data.productName || link.label || article.articlename);                let description = isFeatured ? (data.strapline || '') : (data.text || '');                                if (!isFeatured && !data.productName && data.text) {                   const brSplit = data.text.split(new RegExp('\x3Cbr\\s*\\/?\\x3E', 'i'));                   if (brSplit.length > 1) {                     summaryTitle = brSplit[0].replace(/<[^>]+>/g, '').trim();                     description = brSplit.slice(1).join(' ').replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();                   } else {                     const match = data.text.match(/\x3Cstrong>(.*?)<\/strong>/);                     if (match) {                       summaryTitle = match[1].replace(/<[^>]+>/g, '').trim();                       if (summaryTitle.endsWith(':')) summaryTitle = summaryTitle.slice(0, -1);                     }                   }                }                                let imageUrl = isFeatured ? image.mos : (image.src || null);                if (imageUrl && imageUrl.startsWith('//')) imageUrl = 'https:' + imageUrl;                                description = description.replace(/<br\s*\/?>/gi, ' ').replace(/<\/?(p|div)[^>]*>/gi, ' ').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').replace(/View Deal$/i, '').trim();                                let merchantName = data.retailer || '';                if (!merchantName && externalUrl) {                   try {                     merchantName = new URL(externalUrl).hostname.replace('www.', '').split('.')[0];                     merchantName = merchantName.charAt(0).toUpperCase() + merchantName.slice(1);                   }catch(e){ console.warn(e); }                }                if (!merchantName) merchantName = 'Retailer';                const q = (this.currentQuery || '').toLowerCase();                const activeTagLogic = (this.activeDealTag || '').toLowerCase();                if (q.length > 2 && q !== activeTagLogic) {                   const searchTarget = `${summaryTitle || ''} ${description || ''}`.toLowerCase();                   if (!searchTarget.includes(q)) return;                }                let rawPrice = 0;                let rawMsrp = 0;                let currencyStr = '$';                if (isFeatured) {                   rawPrice = typeof data.salePrice === 'number' && data.salePrice > 0 ? data.salePrice : (typeof data.price === 'number' ? data.price : 0);                   rawMsrp = typeof data.salePrice === 'number' && typeof data.price === 'number' && data.price > data.salePrice ? data.price : 0;                   currencyStr = data.currency === 'GBP' ? '£' : '$';                } else {                   rawPrice = priceObj.amount ? parseFloat(priceObj.amount) : 0;                   rawMsrp = priceObj.amountWas ? parseFloat(priceObj.amountWas) : 0;                   currencyStr = priceObj.currency === 'GBP' ? '£' : '$';                }                                let savingAmt = 0;                let savingLabel = '';                if (rawPrice > 0 && rawMsrp > rawPrice) {                   savingAmt = parseFloat((rawMsrp - rawPrice).toFixed(2));                   savingLabel = `Save ${currencyStr}${savingAmt}`;                }                                // Apply Brand filter                if (this.selectedBrands && this.selectedBrands.length > 0) {                   const itemBrand = (data.brand || '').toLowerCase();                   const hasMatch = this.selectedBrands.some(sb => sb.toLowerCase() === itemBrand);                   if (!hasMatch) return;                }                // Apply Price filter                let priceFilterVal = null;                const min = this.customPriceMin ? this.customPriceMin.value : '';                const max = this.customPriceMax ? this.customPriceMax.value : '';                if (min || max) {                   priceFilterVal = `${min}_${max}`;                } else if (this.priceFilter && this.priceFilter.value !== 'all') {                   priceFilterVal = this.priceFilter.value;                }                if (priceFilterVal && rawPrice > 0) {                   if (priceFilterVal === 'under50' && rawPrice >= 50) return;                   if (priceFilterVal === 'over50' && rawPrice <= 50) return;                   if (priceFilterVal === 'over30' && rawPrice <= 30) return;                   if (priceFilterVal === 'over500' && rawPrice <= 500) return;                   if (priceFilterVal.includes('_')) {                      const parts = priceFilterVal.split('_');                      const min = parseFloat(parts[0]);                      const max = parseFloat(parts[1]);                      if (!isNaN(min) && rawPrice < min) return;                      if (!isNaN(max) && rawPrice > max) return;                   }                }                // Apply Discount filter                if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {                   const requiredDiscount = parseInt(this.discountFilter.value);                   if (!isNaN(requiredDiscount) && requiredDiscount > 0) {                      if (!rawMsrp || rawMsrp <= rawPrice) return;                      const ratio = Math.round((1 - (rawPrice / rawMsrp)) * 100);                      if (ratio < requiredDiscount) return;                   }                }                                extractedDeals.push({                   id: `airedale-${article.id || Math.random()}-${idx}`,                   url: externalUrl,                   image: imageUrl,                   fallbackImage: imageUrl,                   title: summaryTitle,                   brand: data.brand || '',                   productName: data.productName || '',                   merchant: merchantName,                   rawPrice: rawPrice,                   rawMsrp: rawMsrp,                   price: rawPrice > 0 ? rawPrice.toString() : '',                   msrp: rawMsrp > 0 ? rawMsrp.toString() : '',                   currency: currencyStr,                   isCheckPrice: !rawPrice,                   savingLabel: savingLabel,                   savingType: rawMsrp > rawPrice ? 'amount' : 'none',                   isPrime: false,                   starRating: null,                   description: description,                   text: data.text || ''                });             });          });                    const airedaleBrandsList = Object.keys(dynamicBrandsCounts).map(b => ({              formatted_value: b,              count: dynamicBrandsCounts[b]          })).sort((a,b) => b.count - a.count);                    if (this.getViewMode() === 'savings_squad') {             this.populateBrandDropdown(airedaleBrandsList.slice(0, 15));             if (this.brandFilterWrapper) {                if (airedaleBrandsList.length === 0) {                    this.brandFilterWrapper.style.display = 'none';                } else {                    this.brandFilterWrapper.style.display = 'flex';                }             }          }                    this.deals = extractedDeals;          this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        isBroadQuery(query) {          const q = query.toLowerCase();          const intentModifiers = ['deals', 'best', 'sale', 'under', 'cheap', 'offers', 'discount'];          return intentModifiers.some(term => q.includes(term));        }        async fetchHawkDeals(query, append = false) {          const url = new URL(this.apiUrl);          url.searchParams.append('model_name', query);          const areaCode = this.getAreaCode();          if (areaCode) {            url.searchParams.append('area', areaCode);          }                    if (append && this.deals.length > 0) {            url.searchParams.append('offset', this.deals.length.toString());          }                    if (this.retailerSelect && this.retailerSelect.value) {            url.searchParams.append('filter_merchant_name', this.retailerSelect.value);          }                    if (this.selectedBrands && this.selectedBrands.length > 0) {            url.searchParams.append('filter_label[text_brand]', this.selectedBrands.join(','));          }                    let priceVal = null;          const min = this.customPriceMin ? this.customPriceMin.value : '';          const max = this.customPriceMax ? this.customPriceMax.value : '';          if (min || max) {             priceVal = `${min}_${max}`;          } else if (this.priceFilter && this.priceFilter.value !== 'all') {             priceVal = this.priceFilter.value;          }          if (priceVal) {            if (priceVal === 'under50') {              url.searchParams.append('filter_max_price', '50');            } else if (priceVal === 'over50') {              url.searchParams.append('filter_min_price', '50');            } else if (priceVal === 'over30') {              url.searchParams.append('filter_min_price', '30');            } else if (priceVal === 'over500') {              url.searchParams.append('filter_min_price', '500');            } else if (priceVal.includes('_')) {              const parts = priceVal.split('_');              if (parts[0]) url.searchParams.append('filter_min_price', parts[0]);              if (parts[1]) url.searchParams.append('filter_max_price', parts[1]);            }          }                    if (this.discountFilter && this.discountFilter.value !== 'all' && this.discountFilter.value !== '0') {            const v = parseInt(this.discountFilter.value);            if (!isNaN(v) && v > 0) {              const ratio = (100 - v) / 100;              url.searchParams.append('min_discount_ratio', ratio.toString());            }          }                    if (this.offerTypeSelect && this.offerTypeSelect.value) {            url.searchParams.append('offer', this.offerTypeSelect.value);          }                    url.searchParams.append('filter_product_types', 'deals');                    if (this.rowsSelect && this.rowsSelect.value) {            url.searchParams.append('rows', this.rowsSelect.value);          } else {             url.searchParams.append('rows', '12'); // default          }          let response;          try {             response = await fetch(url.toString());          } catch(e) {             if (window.location.protocol === 'file:') {                console.warn("[Tom's Guide Widget] fetch from file:// blocked by local CORS policy, falling back to Adviser mock.");                await this.fetchAdviserDeals(query);                return;             }             console.warn("Hawk fetch failed", e);             this.deals = [];             this.render();             return;          }          if (!response.ok) {            throw new Error('Hawk API Response Error');          }          const rawData = await response.json();          // Safely locate data array from potentially wrapped response          let offers = [];          let modelInfoArray = [];                    let brandFilterData = null;          if (rawData && rawData.widget && rawData.widget.data && Array.isArray(rawData.widget.data.filters)) {             brandFilterData = rawData.widget.data.filters.find(f => f.type === 'label_text_brand');          } else if (rawData && rawData.data && Array.isArray(rawData.data.filters)) {             brandFilterData = rawData.data.filters.find(f => f.type === 'label_text_brand');          }          if (brandFilterData && Array.isArray(brandFilterData.values) && brandFilterData.values.length > 0) {             this.populateBrandDropdown(brandFilterData.values);          } else {             if (this.brandFilterWrapper && this.selectedBrands.length === 0) {                this.brandFilterWrapper.style.display = 'none';             }          }                    if (rawData && rawData.widget && rawData.widget.data) {            if (Array.isArray(rawData.widget.data.offers)) offers = rawData.widget.data.offers;            if (rawData.widget.data.model_info && typeof rawData.widget.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.widget.data.model_info) ? rawData.widget.data.model_info : Object.values(rawData.widget.data.model_info);            }          } else if (rawData && rawData.data) {            if (Array.isArray(rawData.data.offers)) offers = rawData.data.offers;            if (rawData.data.model_info && typeof rawData.data.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.data.model_info) ? rawData.data.model_info : Object.values(rawData.data.model_info);            }          } else {            if (Array.isArray(rawData)) offers = rawData;            else if (rawData && Array.isArray(rawData.offers)) offers = rawData.offers;            else if (rawData && rawData.offers && Array.isArray(rawData.offers.offer)) offers = rawData.offers.offer;            else if (rawData && rawData.offers) offers = [].concat(rawData.offers);                        if (rawData && rawData.model_info && typeof rawData.model_info === 'object') {              modelInfoArray = Array.isArray(rawData.model_info) ? rawData.model_info : Object.values(rawData.model_info);            }          }          let modelDetails = {};          modelInfoArray.forEach(m => {            const mId = m.model_id || m.id;            if (mId) {              modelDetails[mId] = {                score: m.score != null ? parseFloat(m.score) : null,                brand: m.brand || null,                parent: (m.parents && Array.isArray(m.parents) && m.parents.length > 0) ? m.parents[0].name : null              };            }          });          offers.forEach(item => {            let data = { ...item };            const mId = data.model_id;            if (mId && modelDetails[mId]) {              data.review_score = modelDetails[mId].score;              data.model_brand = modelDetails[mId].brand;              data.model_parent = modelDetails[mId].parent;            } else {              data.review_score = null;            }                        let itemOffers = [];            if (Array.isArray(item.offers)) itemOffers = item.offers;            else if (Array.isArray(item.offer)) itemOffers = item.offer;            else if (item.offers && typeof item.offers === 'object') itemOffers = [item.offers];            else if (item.offer && typeof item.offer === 'object') itemOffers = [item.offer];            if (itemOffers.length > 0) {              itemOffers.forEach(subItem => {                let subData = { ...item, ...subItem };                const subId = subData.model_id;                if (subId && modelDetails[subId]) {                  subData.review_score = modelDetails[subId].score;                  subData.model_brand = modelDetails[subId].brand;                  subData.model_parent = modelDetails[subId].parent;                } else if (data.review_score != null) {                  subData.review_score = data.review_score;                }                if (subData.merchant && typeof subData.merchant === 'object') {                  subData.merchant_name = subData.merchant.name;                }                this.deals.push(this.extractDealData(subData));              });              return;            }                        if (item.merchant && typeof item.merchant === 'object') {              data.merchant_name = item.merchant.name;            }                        this.deals.push(this.extractDealData(data));          });                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        async fetchAdviserDeals(query) {          // ======================================================================          // TODO: ADVISER API REPLACEMENT          // The code below simulates the Adviser API response using mock data.          // Once the real endpoint is ready, remove getAdviserMockData() and           // perform an actual fetch() request similar to fetchHawkDeals().          // Example:          // const area = this.getAreaCode();          // let apiUrl = `https://your-adviser-api.com/search?q=${query}&area=${area}`;          // if (this.priceFilter && this.priceFilter.value !== 'all') {          //   const val = this.priceFilter.value;          //   if (val === 'under50') apiUrl += '&filter_max_price=50';          //   else if (val === '50_100') apiUrl += '&filter_max_price=100';          //   else if (val === '100_200') apiUrl += '&filter_max_price=200';          //   else if (val === '200_500') apiUrl += '&filter_max_price=500';          // }          // const res = await fetch(apiUrl);          // const rawData = await res.json();          // ======================================================================          // Simulating network latency          await new Promise(resolve => setTimeout(resolve, 400));                    const rawData = this.getAdviserMockData();          let offers = [];                    if (rawData && rawData.data && rawData.data.Get && Array.isArray(rawData.data.Get.Deal)) {            offers = rawData.data.Get.Deal;          }                    // Basic client-side filtering for the mock if we want it to react to the query          const q = query.toLowerCase();          const selectedRetailer = (this.retailerSelect && this.retailerSelect.value) ? this.retailerSelect.value.toLowerCase() : null;                    offers.forEach(item => {            const dataObj = item;                        // Apply retailer filter            const itemRetailer = (dataObj.dataRetailer || '').toLowerCase();            if (selectedRetailer && itemRetailer !== selectedRetailer && !itemRetailer.includes(selectedRetailer)) {              return;            }                        // Apply mock price filter            let price = dataObj.dataDiscountedPrice || 0;            if (typeof price === 'string') {              price = parseFloat(price.replace(/[^0-9.]/g, ''));            }            let priceVal = null;            const min = this.customPriceMin ? this.customPriceMin.value : '';            const max = this.customPriceMax ? this.customPriceMax.value : '';            if (min || max) {               priceVal = `${min}_${max}`;            } else if (this.priceFilter && this.priceFilter.value !== 'all') {               priceVal = this.priceFilter.value;            }            if (priceVal) {              if (priceVal === 'under50' && price >= 50) return;              if (priceVal === 'over50' && price <= 50) return;              if (priceVal === 'over30' && price <= 30) return;              if (priceVal === 'over500' && price <= 500) return;              if (priceVal.includes('_')) {                 const parts = priceVal.split('_');                 if (parts[0] && price < parseFloat(parts[0])) return;                 if (parts[1] && price > parseFloat(parts[1])) return;              }            }                        // Map Adviser schema to our widget's expected schema            const mappedData = {              url: dataObj.linkHREF || dataObj.dataLink || '#',              image: dataObj.imageURL || (dataObj.image && dataObj.image.src) || '',              title: dataObj.dataProduct || (dataObj.product && dataObj.product.name) || 'Product Deal',              merchant: dataObj.dataRetailer || 'Retailer',              price: dataObj.dataDiscountedPrice || 0,              currency: dataObj.dataCurrency === 'USD' ? '$' : (dataObj.dataCurrency || '$'),              msrp: dataObj.dataOriginalPrice || null            };                        const titleLow = mappedData.title.toLowerCase();            const merchLow = mappedData.merchant.toLowerCase();                        // Smarter mock filtering            let isMatch = false;            if (q === '' || this.isBroadQuery(q)) {              isMatch = true;            } else if (titleLow.includes(q) || merchLow.includes(q)) {              isMatch = true;            } else if ((q.includes('laptop') || q.includes('mac') || q.includes('pc')) && (titleLow.includes('macbook') || titleLow.includes('laptop'))) {              isMatch = true;            } else if ((q.includes('tv') || q.includes('television')) && (titleLow.includes('tv') || titleLow.includes('oled') || titleLow.includes('qled'))) {              isMatch = true;            } else if ((q.includes('phone') || q.includes('smartphone')) && (titleLow.includes('galaxy') || titleLow.includes('phone'))) {              isMatch = true;            } else if ((q.match(/watch|fitness|run|shoe/)) && (titleLow.includes('forerunner') || titleLow.includes('saucony') || titleLow.includes('watch'))) {              isMatch = true;            }                        if (isMatch) {               this.deals.push(this.extractDealData(mappedData));            }          });                    let rowLimit = 12;          if (this.rowsSelect && this.rowsSelect.value) {            rowLimit = parseInt(this.rowsSelect.value, 10) || 12;          }          // Intentionally omitting the slice here to allow "Load More" to work if the API returns more                    this.sortData();          this.render();          if (typeof trackDealsAppeared !== 'undefined') {             trackDealsAppeared(this.widgetId, this.deals, this.revenueId, typeof this.getAreaCode === 'function' ? (this.getAreaCode() === 'GB' ? 'GBP' : 'USD') : 'USD', this.currentQuery, this.widgetTypeName);          }        }        getAdviserMockData() {          return {            "data": {              "Get": {                "Deal": [                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 300,                    "dataOriginalPrice": 399,                    "dataProduct": "Samsung Galaxy A36",                    "dataRetailer": "Samsung",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/MqDYsukV3JBG54te6dEs7j.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 14,                    "dataOriginalPrice": 24,                    "dataProduct": "Blink Mini",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/3JurmAjHsDa5tPdaHAwEV8.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 59,                    "dataOriginalPrice": 99,                    "dataProduct": "Ring Video Doorbell",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/rAh4uR7AsAsALCCLTXnLNJ.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 10,                    "dataOriginalPrice": 599,                    "dataProduct": "MacBook Neo",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/Lg4Dvg68j9SbB5CPNrTEpH.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 749,                    "dataOriginalPrice": 849,                    "dataProduct": "65\\\" Fire TV Omni 4K QLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/SG34ZWodUkLTxJvMTbjPYR.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 71,                    "dataOriginalPrice": 160,                    "dataProduct": "Saucony Hurricane 24",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/vxf7UD5T2Am7guVzFoFcZ4.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 649,                    "dataOriginalPrice": 749,                    "dataProduct": "Garmin Forerunner 970",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/3GKnEu7CdhtxPMfnPCMCiA.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1049,                    "dataOriginalPrice": 1499,                    "dataProduct": "LG 48\\\" C4 4K OLED TV",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/imvwZV9zoMD6fn9Afuge35.jpg"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 1499,                    "dataOriginalPrice": 2199,                    "dataProduct": "Samsung 49\\\" Odyssey Neo G9 4K Gaming Monitor",                    "dataRetailer": "Amazon",                    "imageURL": "http://cdn.mos.cms.futurecdn.net/XWDEJ5dUAE2nhK8k3Jk7k7.png"                  },                  {                    "dataCurrency": "USD",                    "dataDiscountedPrice": 299,                    "dataOriginalPrice": 699,                    "dataProduct": "EGOHOME Black Memory Foam Mattress (queen)",                    "dataRetailer": "Amazon",                    "imageURL": "https://cdn.mos.cms.futurecdn.net/hMUemtAejNETLVYxNrktzm.jpg"                  }                ]              }            }          };        }        decodeHTML(html) {          if (!html) return '';          const txt = document.createElement("textarea");          txt.innerHTML = String(html);          return txt.value;        }        extractDealData(item) {          const priceRawStr = String(item.price || item.current_price || '0');          const msrpRawStr = String(item.was_price || item.msrp || item.original_price || '0');          const rawPrice = parseFloat(priceRawStr.replace(/[^\d.]/g, '')) || 0;          const rawMsrp = parseFloat(msrpRawStr.replace(/[^\d.]/g, '')) || 0;          const isCheckPrice = rawPrice === 0 || priceRawStr === '0.00' || priceRawStr === '0';                    let originalImageUrl = item.image || item.image_url || item.product_image || '';          let imageUrl = originalImageUrl;          if ((!imageUrl || isCheckPrice) && item.model_image_url) {             imageUrl = item.model_image_url;             originalImageUrl = imageUrl;          } else if ((!imageUrl || isCheckPrice) && item.model_image) {             imageUrl = item.model_image;             originalImageUrl = imageUrl;          }                    if (imageUrl) {            imageUrl = imageUrl.replace(/-(\d+)-(\d+)(\.[a-z.]+)$/i, '$3');          }                    let fallbackImage = '';          if (originalImageUrl && originalImageUrl !== imageUrl) {             fallbackImage = originalImageUrl;          } else if (item.model_image && item.model_image !== imageUrl) {             fallbackImage = item.model_image;          } else if (item.model_image_url && item.model_image_url !== imageUrl) {             fallbackImage = item.model_image_url;          }                    const rawCurrency = item.currency || item.currency_symbol || '$';                    let savingLabel = item.percentage_saving_label || '';          if (!savingLabel && rawMsrp > rawPrice && rawPrice > 0) {            const pct = Math.round(((rawMsrp - rawPrice) / rawMsrp) * 100);            if (pct > 0) {              savingLabel = `${pct}% OFF`;            }          }                    const isPrime = item.shipping && item.shipping.prime === true;                    let scoreRaw = (item.review_score !== undefined && item.review_score !== null && item.review_score > 0) ? parseFloat(item.review_score) : null;          let starRating = 0;          if (scoreRaw !== null) {            starRating = Math.round((scoreRaw > 10 ? scoreRaw / 20 : scoreRaw / 2) * 2) / 2;          }                    return {            id: item.offer_id || item.link || item.url || item.offer_link || Math.random().toString(),            url: item.link || item.url || item.offer_link || '#',            image: imageUrl,            fallbackImage: fallbackImage,            title: item.name || item.title || item.model_name || item.product_name || 'Unknown Product',            brand: item.brand || '',            productName: item.model_name || item.product_name || item.name || '',            merchant: item.merchant_name || item.merchant || item.retailer || 'Retailer',            price: item.price !== undefined ? String(item.price) : '0.00',            currency: this.decodeHTML(rawCurrency),            msrp: item.was_price || item.msrp || item.original_price || null,            rawPrice: rawPrice,            rawMsrp: rawMsrp,            hasWasPrice: (item.was_price !== undefined && item.was_price !== null),            isCheckPrice: isCheckPrice,            savingLabel: savingLabel,            isPrime: isPrime,            starRating: starRating > 0 ? starRating : null,            modelId: item.model_id || '',            productKey: item.product_key || '',            merchantId: (item.merchant && typeof item.merchant === 'object') ? item.merchant.id || '' : '',            matchId: item.match_id || '',            merchantNetwork: (item.merchant && typeof item.merchant === 'object') ? item.merchant.an || '' : '',            merchantUrl: (item.merchant && typeof item.merchant === 'object') ? item.merchant.url || '' : '',            modelBrand: item.model_brand || item.brand || '',            modelParent: item.model_parent || ''          };        }        sortData() {          const sortVal = this.sortSelect ? this.sortSelect.value : (this.getViewMode() === 'savings_squad' ? 'date_desc' : 'discount_desc');          if (sortVal === 'price_asc') {            this.deals.sort((a, b) => a.rawPrice - b.rawPrice);          } else if (sortVal === 'price_desc') {            this.deals.sort((a, b) => b.rawPrice - a.rawPrice);          } else if (sortVal === 'discount_desc') {            this.deals.sort((a, b) => {              const aDiscount = a.rawMsrp > a.rawPrice ? (a.rawMsrp - a.rawPrice) : 0;              const bDiscount = b.rawMsrp > b.rawPrice ? (b.rawMsrp - b.rawPrice) : 0;              return bDiscount - aDiscount;            });          } else if (sortVal === 'date_desc') {             this.deals.sort((a, b) => {                let dateA = 0;                let dateB = 0;                if (a && a.modifiedDate) {                   const valA = Array.isArray(a.modifiedDate) ? a.modifiedDate[0] : a.modifiedDate;                   dateA = new Date(valA).getTime();                   if (isNaN(dateA)) dateA = 0;                }                if (b && b.modifiedDate) {                   const valB = Array.isArray(b.modifiedDate) ? b.modifiedDate[0] : b.modifiedDate;                   dateB = new Date(valB).getTime();                   if (isNaN(dateB)) dateB = 0;                }                return dateB - dateA;             });          }        }        getFilteredDeals() {          let filteredDeals = [...this.deals];                    if (this.dealModeToggle && this.dealModeToggle.checked) {            filteredDeals = filteredDeals.filter(d => d.hasWasPrice || (d.msrp && d.rawMsrp > d.rawPrice));          }                    return filteredDeals;        }        showLoading() {          const _div = '<' + '/div>';          const skeletonCardHtml = `            \x3Cdiv class="tg-df-card">              \x3Cdiv class="tg-df-card-image-box">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-img">${_div}              ${_div}              \x3Cdiv class="tg-df-card-body">                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text title">${_div}                \x3Cdiv class="tg-df-card-footer mt-auto">                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text short" style="height:24px;">${_div}                  \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text" style="height:44px; margin-top:8px;">${_div}                ${_div}              ${_div}            ${_div}`;          this.grid.innerHTML = Array(4).fill(skeletonCardHtml).join('');        }        showError() {          const _div = '<' + '/div>';          this.grid.innerHTML = `\x3Cdiv class="tg-df-message">            An error occurred while finding deals. Please check your connection and try again.          ${_div}`;        }        escapeHTML(str) {          if (!str) return '';          return String(str).replace(/[&<>'"]/g, tag => ({              '&': '&', '<': '<', '>': '>', "'": ''', '"': '"'          }[tag] || tag));        }                bindCouponButtons() {          const btns = this.root.querySelectorAll('.tg-df-tag-coupons');          btns.forEach(btn => {            btn.addEventListener('click', (e) => {              e.preventDefault();              e.stopPropagation();              const merchant = btn.getAttribute('data-merchant');              this.openVouchersModal(merchant);            });          });                    const closeBtn = this.root.querySelector('#tg-df-vouchers-close');          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (closeBtn) {            closeBtn.onclick = () => this.closeVouchersModal();          }          if (backdrop) {            backdrop.onclick = (e) => {              if (e.target === backdrop) this.closeVouchersModal();            };          }        }                closeVouchersModal() {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          if (backdrop) backdrop.classList.remove('active');        }                async checkMerchantsCouponsBulk(merchants) {          if (!merchants || merchants.length === 0) return {};          const controller = new AbortController();          const timeoutId = setTimeout(() => controller.abort(), 4000);          try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchants.join(','));            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '120');            url.searchParams.append('origin', 'widgets-clientside');                        let res; try { res = await fetch(url.toString(), { signal: controller.signal }); } catch (e) { return {}; }            clearTimeout(timeoutId);            if (!res.ok) return {};            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        const foundMerchants = new Set();            offers.forEach(o => {              let mName = o.merchant_name || o.merchant || o.retailer;              if (mName && typeof mName === 'object') mName = mName.name;              if (mName) foundMerchants.add(String(mName).toLowerCase());            });            const resultMap = {};            merchants.forEach(m => {              if (m) resultMap[m] = foundMerchants.has(String(m).toLowerCase());            });            return resultMap;          } catch (e) {            return {};          }        }                async openVouchersModal(merchantName) {          const backdrop = this.root.querySelector('#tg-df-vouchers-modal');          const title = this.root.querySelector('#tg-df-vouchers-title');          const content = this.root.querySelector('#tg-df-vouchers-content');                    if (!backdrop || !content) return;                    // HACK: Hide closing tags          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h4 = '<' + '/h4>';          const _svg = '<' + '/svg>';          const _circle = '<' + '/circle>';          const _polyline = '<' + '/polyline>';          const _rect = '<' + '/rect>';          const _path = '<' + '/path>';                    title.innerText = `${merchantName} Coupons & Deals`;          content.innerHTML = `\x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}                               \x3Cdiv class="tg-df-skeleton tg-df-skeleton-text">${_div}`;          backdrop.classList.add('active');                    try {            const area = this.getAreaCode();            const url = new URL('https://search-api.fie.future.net.uk/widget.php');            url.searchParams.append('model_name', 'Everything');            url.searchParams.append('language', 'en-GB');            if (area) url.searchParams.append('area', area);            url.searchParams.append('combine_product_types', '1');            url.searchParams.append('filter_merchant_name', merchantName);            url.searchParams.append('all_filters', 'false');            url.searchParams.append('exclude_unlabelled', 'false');            url.searchParams.append('include_specs', 'false');            url.searchParams.append('sort', 'voucher');            url.searchParams.append('distinct_merchants', 'natural');            url.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');            url.searchParams.append('rows', '50');            url.searchParams.append('origin', 'widgets-clientside');                        const res = await fetch(url.toString());            if (!res.ok) throw new Error('API Error');            const data = await res.json();                        let offers = [];            if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {              offers = data.widget.data.offers;            } else if (data && data.data && Array.isArray(data.data.offers)) {              offers = data.data.offers;            } else if (Array.isArray(data)) {              offers = data;            } else if (data && Array.isArray(data.offers)) {              offers = data.offers;            } else if (data && data.offers && Array.isArray(data.offers.offer)) {              offers = data.offers.offer;            } else if (data && Array.isArray(data.data)) {              offers = data.data;            }                        if (offers.length === 0) {              content.innerHTML = `\x3Cdiv class="tg-df-message">No vouchers currently available for ${this.escapeHTML(merchantName)}.${_div}`;              return;            }                        content.innerHTML = offers.map(v => {              let offerObj = v;              if (v.offers && v.offers.offer) {                offerObj = Array.isArray(v.offers.offer) ? v.offers.offer[0] : v.offers.offer;              } else if (v.offer) {                offerObj = Array.isArray(v.offer) ? v.offer[0] : v.offer;              }              let logoUrl = v.logo_url || offerObj.logo_url || '';              if (!logoUrl && v.merchant) {                if (Array.isArray(v.merchant) && v.merchant.length > 0) logoUrl = v.merchant[0].logo_url || '';                else logoUrl = v.merchant.logo_url || '';              }                            const offerName = offerObj.name || offerObj.title || v.name || v.title || 'Special Offer';              const endTime = offerObj.end_time || v.end_time || '';              const linkUrl = offerObj.link || offerObj.url || v.link || v.url || '#';                            let foundVoucherCode = '';              const findVoucherCode = (obj) => {                if (!obj || typeof obj !== 'object') return;                if (obj.type === 'voucher_code' && obj.display_value) {                  foundVoucherCode = obj.display_value;                  return;                }                if (Array.isArray(obj)) {                  for (const item of obj) {                    findVoucherCode(item);                    if (foundVoucherCode) return;                  }                } else {                  for (const k in obj) {                    if (Object.prototype.hasOwnProperty.call(obj, k)) {                      findVoucherCode(obj[k]);                      if (foundVoucherCode) return;                    }                  }                }              };              findVoucherCode(offerObj);              if (!foundVoucherCode) findVoucherCode(v);                            const voucherCode = foundVoucherCode || offerObj.voucher_code || v.voucher_code || '';              const codeHtml = voucherCode ? `\x3Cspan class="tg-df-voucher-code" data-action="copy-code" data-code="${this.escapeHTML(voucherCode)}" title="Copy to clipboard">                \x3Cspan class="tg-df-voucher-code-text">${this.escapeHTML(voucherCode)}${_span}                \x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-left:6px;flex-shrink:0;" class="tg-df-voucher-copy-icon">                  \x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2">${_rect}                  \x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1">${_path}                ${_svg}              ${_span}` : '';                            const logoHtml = logoUrl                 ? `\x3Cimg src="${this.escapeHTML(logoUrl)}" alt="${this.escapeHTML(offerName)}" class="tg-df-voucher-logo" />`                 : `\x3Cdiv class="tg-df-voucher-logo" style="background:#e2e8f0;">${_div}`;                            let expiryHtml = '';              if (endTime) {                let dStr = endTime;                if (!isNaN(dStr) && String(dStr).length === 10) dStr = Number(dStr) * 1000;                const d = new Date(dStr);                if (!isNaN(d.getTime())) {                  const options = { year: 'numeric', month: 'short', day: 'numeric' };                  expiryHtml = `                    \x3Cdiv class="tg-df-voucher-expiry">                      \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                        \x3Ccircle cx="12" cy="12" r="10">${_circle}                        \x3Cpolyline points="12 6 12 12 16 14">${_polyline}                      ${_svg}                      Expires ${d.toLocaleDateString(undefined, options)}                    ${_div}`;                }              }              return `                \x3Ca href="${this.escapeHTML(linkUrl)}" target="_blank" rel="noopener nofollow" class="tg-df-voucher-item">                  ${logoHtml}                  \x3Cdiv class="tg-df-voucher-content">                    \x3Ch4 class="tg-df-voucher-title">${this.escapeHTML(offerName)}${_h4}                    ${codeHtml}                    ${expiryHtml}                  ${_div}                ${_a}              `;            }).join('');                        // Attach copy functionality            const copyBtns = content.querySelectorAll('[data-action="copy-code"]');            copyBtns.forEach(btn => {              btn.addEventListener('click', async (e) => {                e.preventDefault();                e.stopPropagation();                                const code = btn.getAttribute('data-code');                if (!code) return;                                try {                  const copyToClipboard = async (text) => {                     if (window.navigator.clipboard && window.isSecureContext) {                        try { await window.navigator.clipboard.writeText(text); return; } catch (e) {}                     }                     const textArea = document.createElement("textarea");                     textArea.value = text;                     textArea.style.position = "fixed";                     document.body.appendChild(textArea);                     textArea.focus();                     textArea.select();                     document.execCommand('copy');                     textArea.remove();                  };                  await copyToClipboard(code);                                    // Visual feedback                  btn.classList.add('copied');                  const textSpan = btn.querySelector('.tg-df-voucher-code-text');                  const iconSvg = btn.querySelector('.tg-df-voucher-copy-icon');                                    const origText = textSpan.innerText;                  const origIcon = iconSvg.innerHTML;                                    textSpan.innerText = 'Copied!';                  iconSvg.innerHTML = `\x3Cpolyline points="20 6 9 17 4 12">${_polyline}`;                                    setTimeout(() => {                    if (btn) {                      btn.classList.remove('copied');                      if (textSpan) textSpan.innerText = origText;                      if (iconSvg) iconSvg.innerHTML = origIcon;                    }                  }, 2000);                                    trackElementInteraction({                    id: 'voucher-code-copy',                    name: 'Copy Voucher Code',                    label: `Copied ${code} for ${merchantName}`                  });                } catch (err) {                  console.warn('Failed to copy text: ', err);                }              });            });                                  } catch (e) {            console.warn(e);            content.innerHTML = `\x3Cdiv class="tg-df-message">Failed to load vouchers.${_div}`;          }        }        render() {          try {            if (this.getViewMode() === 'savings_squad' && this.airedaleTags.length > 0) {              if (this.categoryFilterWrapper) {                 this.categoryFilterWrapper.style.display = 'flex';              }              if (this.categoryFilter) {                 const _option = '<' + '/option>';                 let optionsHtml = `\x3Coption value="all">All Categories${_option}`;                 this.airedaleTags.forEach(tag => {                    const isSelected = this.activeDealTag === tag ? 'selected' : '';                    optionsHtml += `\x3Coption value="${this.escapeHTML(tag)}" ${isSelected}>${this.escapeHTML(tag)} (${this.airedaleTagCounts[tag] || 0})${_option}`;                 });                 this.categoryFilter.innerHTML = optionsHtml;                 this.categoryFilter.value = this.activeDealTag || 'all';              }            } else {               if (this.categoryFilterWrapper) {                  this.categoryFilterWrapper.style.display = 'none';               }            }            const displayDeals = this.getFilteredDeals();          // HACK: Hide closing tags from the CMS HTML sanitizer so it doesn't strip them during in-page injection          const _div = '<' + '/div>';          const _span = '<' + '/span>';          const _a = '<' + '/a>';          const _h3 = '<' + '/h3>';          const _p = '<' + '/p>';          const _strong = '<' + '/strong>';          const _sup = '<' + '/sup>';          const _button = '<' + '/button>';          if (displayDeals.length === 0) {            if (this.currentQuery.length > 2 || (this.getViewMode() === 'savings_squad')) {              if (this.deals.length > 0) {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No deals match your selected filters.                ${_div}`;              } else if (this.getViewMode() === 'savings_squad' && this.currentQuery.length <= 2) {                 // Do not show "no exact matches" if query is empty for savings_squad                 this.grid.innerHTML = '';              } else {                 this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                  No exact matches found for "\x3Cstrong>${this.escapeHTML(this.currentQuery)}${_strong}". Try adjusting your search term.                ${_div}`;              }            } else {              this.grid.innerHTML = `\x3Cdiv class="tg-df-message">                Search product or category names to discover the best deals from across the web.              ${_div}`;            }            return;          }          let dealsHtml = displayDeals.slice(0, this.displayLimit).map((deal, index) => {            try {               const currencySym = this.escapeHTML(deal.currency);               const isoCurrencyCode = normalizeCurrency(currencySym);               const escapedPrice = this.escapeHTML(deal.price);               const escapedMsrp = this.escapeHTML(deal.msrp);               const areaCode = this.getAreaCode();                              const revenueId = generateRevenueId(deal.url, deal.title, deal.merchant, null);               const originalLink = deal.url;               const rewrittenLink = rewriteAffiliateLink(deal.url, areaCode, revenueId);                        const productCategoryName = 'deals';            const dataAttr = `              data-action="${deal.isCheckPrice ? 'view-similar-click' : 'deal-click'}"              data-analytics-id="${this.escapeHTML(deal.externalProductId || deal.id || '')}"              data-product-name="${this.escapeHTML(deal.title)}"              data-merchant-name="${this.escapeHTML(deal.merchant)}"              data-price="${deal.rawPrice || ''}"              data-previous-price="${deal.rawMsrp || ''}"              data-original-link="${this.escapeHTML(originalLink)}"              data-revenue-id="${revenueId}"              data-index="${index}"              data-total="${displayDeals.length}"              data-in-stock="${deal.inStock !== false}"              data-currency="${this.escapeHTML(isoCurrencyCode)}"              data-model-id="${this.escapeHTML(deal.modelId || '')}"              data-product-key="${this.escapeHTML(deal.productKey || '')}"              data-merchant-id="${this.escapeHTML(deal.merchantId || '')}"            `;                        let priceGroupHtml = '';            let isSavingsSquadMode = this.getViewMode() === 'savings_squad';            let ctaText = 'View Deal';            let formattedPrice = '';            let msrpHtml = '';                        if (deal.isCheckPrice) {              ctaText = isSavingsSquadMode ? 'View Deal' : 'Check Price';              if (isSavingsSquadMode) {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                  ${_div}                `;              } else {                priceGroupHtml = `                  \x3Cdiv class="tg-df-card-merchant-wrapper">                    \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                  ${_div}                  \x3Cdiv class="tg-df-card-price-group">                    \x3Cspan class="tg-df-card-price" style="font-size: 15px; font-weight: 500; font-style: italic;">See price at retailer${_span}                  ${_div}                `;              }            } else {              // Format Price              formattedPrice = escapedPrice.includes(currencySym)                 ? escapedPrice                 : `${currencySym}${escapedPrice}`;                              // Format MSRP              msrpHtml = deal.msrp && deal.rawMsrp > deal.rawPrice                ? `\x3Cspan class="tg-df-card-msrp">${escapedMsrp.includes(currencySym) ? escapedMsrp : currencySym + escapedMsrp}${_span}`                : '';                              priceGroupHtml = `                \x3Cdiv class="tg-df-card-merchant-wrapper">                  \x3Cspan class="tg-df-card-merchant-pill" title="${this.escapeHTML(deal.merchant)}">${this.escapeHTML(deal.merchant)}${_span}                ${_div}                \x3Cdiv class="tg-df-card-price-group">                  ${isSavingsSquadMode ? '' : `                  \x3Cspan class="tg-df-card-price">${formattedPrice}${_span}                  ${msrpHtml}                  `}                ${_div}              `;            }                        const discountBadgeHtml = deal.savingLabel && !deal.isCheckPrice              ? `\x3Cspan class="tg-df-card-discount-badge">${this.escapeHTML(deal.savingLabel)}${_span}`              : '';                          // HACK for CMS            const _button = '<' + '/button>';            const _svg = '<' + '/svg>';            const _path = '<' + '/path>';            const _rect = '<' + '/rect>';            const _circle = '<' + '/circle>';            const _polyline = '<' + '/polyline>';            const _line = '<' + '/line>';                        let badgesHtml = '';            const primeBadge = deal.isPrime ? `              \x3Cspan class="tg-df-tag tg-df-tag-prime">                \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">                  \x3Cpath d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z">${_path}                ${_svg} Prime              ${_span}            ` : '';                        const couponsBadge = `              \x3Cdiv class="tg-df-coupon-wrapper" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:inline-flex; align-items:center;">                \x3Cdiv class="tg-df-coupon-spinner">${_div}                \x3Cbutton type="button" class="tg-df-tag tg-df-tag-coupons" data-action="coupons-click" data-merchant="${this.escapeHTML(deal.merchant)}" style="display:none;">                  \x3Csvg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">                    \x3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z">${_path}                    \x3Cline x1="7" y1="7" x2="7.01" y2="7">${_line}                  ${_svg} Coupons                ${_button}              ${_div}            `;                        // Note: We always add coupons badge if there's a chance, but to allow 3-line titles we check wrapper display state            badgesHtml = `              \x3Cdiv class="tg-df-card-badges">                ${primeBadge}                ${couponsBadge}              ${_div}            `;            const _linearGradient = '<' + '/linearGradient>';            const _polygon = '<' + '/polygon>';            const _stop = '<' + '/stop>';            const _defs = '<' + '/defs>';                        let starHtml = '';            if (deal.starRating) {              let rating = deal.starRating;                            if (rating > 0) {                const fullStars = Math.floor(rating);                const halfStar = (rating - fullStars) >= 0.5 ? 1 : 0;                const emptyStars = Math.max(0, 5 - fullStars - halfStar);                const blue = '#1f69ff'; // Tom's guide brand color from VIEW DEAL button                const gray = '#cbd5e1';                                const starSvgFull = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="${blue}" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                const gradId = 'half_grad_' + Math.floor(Math.random()*1000000);                const starSvgHalf = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" stroke="${blue}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cdefs>\x3ClinearGradient id="${gradId}" x1="0" x2="1" y1="0" y2="0">\x3Cstop offset="50%" stop-color="${blue}">${_stop}\x3Cstop offset="50%" stop-color="transparent">${_stop}${_linearGradient}${_defs}                  \x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26" fill="url(#${gradId})">${_polygon}${_svg}`;                                  const starSvgEmpty = `\x3Csvg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="${gray}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26">${_polygon}${_svg}`;                                let stars = [];                for (let i=0; i<fullStars; i++) stars.push(starSvgFull);                if (halfStar) stars.push(starSvgHalf);                for (let i=0; i<emptyStars; i++) stars.push(starSvgEmpty);                                starHtml = `\x3Cdiv class="tg-df-card-stars" style="display:flex;align-items:center;margin-bottom:8px;font-size:13px;font-weight:600;color:var(--tg-df-text-muted);">                  \x3Cspan style="margin-right:6px;">Tom's Guide:${_span}                  \x3Cdiv style="display:flex;gap:2px;">                    ${stars.join('')}                  ${_div}                ${_div}`;              }            }            let htmlOutput = '';            if (isSavingsSquadMode) {              htmlOutput += `              \x3Cdiv class="hawk-deal-widget-container tg-df-mobile-only" data-collapsible="true">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''} style="margin-bottom: 10px;">` : ''}                \x3Cdiv class="hawk-deal-widget-wrap">                  \x3Cdiv class="hawk-deal-widget-image-container">                    \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" rel="sponsored noopener" target="_blank" class="hawk-affiliate-link-deal-widget" ${dataAttr}>                      \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="hawk-lazy-image-deal-widget" loading="lazy" width="140" height="160" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                    ${_a}                  ${_div}                  \x3Cdiv class="hawk-deal-widget-text-cta-container">                    \x3Cdiv class="hawk-deal-widget-text-body-container">                      \x3Cdiv class="hawk-deal-widget-text-body-main">                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          ${deal.isCheckPrice ? `                            \x3Cspan class="hawk-deal-widget-title-product-title">${this.escapeHTML(deal.title)}${_span}                          ` : `                            \x3Cspan class="hawk-deal-widget-title-product-title">${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_span}                          `}                        ${_a}                        ${!deal.isCheckPrice && deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan class="hawk-deal-widget-title-was-price">was ${currencySym}${escapedMsrp}${_span}                          ${_a}                        ` : ''}                        \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-container" rel="sponsored noopener" target="_blank" ${dataAttr}>                          \x3Cspan class="hawk-deal-widget-title-retailer-price">                            ${!deal.isCheckPrice ? `                              \x3Cspan class="hawk-deal-widget-title-price">now ${formattedPrice}${_span}                              \x3Cspan class="hawk-deal-widget-title-retailer"> at ${this.escapeHTML(deal.merchant)}${_span}                            ` : `                              \x3Cspan class="hawk-deal-widget-title-price">See price at ${this.escapeHTML(deal.merchant)}${_span}                            `}                          ${_span}                        ${_a}                        ${deal.description ? `\x3Cdiv class="hawk-deal-widget-text-body-description">\x3Cp>${this.escapeHTML(deal.description)}${_p}${_div}` : ''}                      ${_div}                    ${_div}                    \x3Cdiv class="hawk-deal-widget-footer">                      \x3Cdiv class="hawk-deal-widget-button-wrapper">                        \x3Cdiv class="hawk-deal-widget-preferred-partner-wrapper">                          \x3Ca data-google-interstitial="false" aria-label="View ${this.escapeHTML(deal.title)} on ${this.escapeHTML(deal.merchant)}" href="${this.escapeHTML(rewrittenLink)}" class="hawk-affiliate-link-deal-button" rel="sponsored noopener" target="_blank" ${dataAttr}>                            \x3Cspan>${deal.isCheckPrice ? 'Check Price' : 'View Deal'}${_span}                          ${_a}                        ${_div}                      ${_div}                    ${_div}                  ${_div}                ${_div}              ${_div}              `;            }            htmlOutput += `              \x3Cdiv class="tg-df-card ${isSavingsSquadMode ? 'tg-df-desktop-only' : ''}">                ${this.editorMode ? `\x3Cinput type="checkbox" class="tg-df-deal-checkbox" data-id="${this.escapeHTML(deal.id)}" ${this.selectedDeals.has(deal.id) ? 'checked' : ''}>` : ''}                \x3Cdiv class="tg-df-card-image-box">                  ${discountBadgeHtml}                  \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;">                    \x3Cimg ${deal.image ? `src="${this.escapeHTML(deal.image)}"` : ''} alt="${this.escapeHTML(deal.title)}" class="tg-df-card-image" loading="lazy" onerror="${deal.fallbackImage ? `if(!this.dataset.fb) { this.dataset.fb='1'; this.src='${this.escapeHTML(deal.fallbackImage)}'; } else { this.style.opacity='0'; }` : `this.style.opacity='0';`}">                  ${_a}                ${_div}                \x3Cdiv class="tg-df-card-body">                  ${starHtml}                  ${badgesHtml}                  \x3Ch3 class="tg-df-card-title tg-df-custom-savings-squad-title" title="${this.escapeHTML(deal.title)}">                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" disable-tracking="true" target="_blank" rel="noopener nofollow" style="text-decoration: none; color: inherit;">                      ${isSavingsSquadMode                         ? (deal.isCheckPrice                             ? (deal.title && deal.title.includes(':')                                 ? `\x3Cstrong>${this.escapeHTML(deal.title.substring(0, deal.title.indexOf(':') + 1))}${_strong}\x3Cspan style="color: #1f69ff; font-weight: normal;">${this.escapeHTML(deal.title.substring(deal.title.indexOf(':') + 1))}${_span}`                                : this.escapeHTML(deal.title)                              )                             : `\x3Cstrong>${deal.brand ? this.escapeHTML(deal.brand) + ' ' : ''}${this.escapeHTML(deal.productName || deal.title || '')}:${_strong} ${deal.rawMsrp && deal.rawMsrp > deal.rawPrice ? `\x3Cspan style="color: #d0021b; text-decoration: line-through; font-weight: normal; margin-right: 4px;">was ${currencySym}${escapedMsrp}${_span} ` : ''}\x3Cspan style="color: #1f69ff; font-weight: normal;">now ${formattedPrice} at ${this.escapeHTML(deal.merchant)}${_span}`                          )                        : this.escapeHTML(deal.title)                      }                    ${_a}                  ${_h3}                  ${deal.description ? `\x3Cp style="font-size: 13px; color: var(--tg-df-text-muted); margin-bottom: 12px; line-height: 1.4;">${this.escapeHTML(deal.description)}${_p}` : ''}                  \x3Cdiv class="tg-df-card-footer">                    ${priceGroupHtml}                    \x3Ca href="${this.escapeHTML(rewrittenLink)}" ${dataAttr} target="_blank" rel="noopener nofollow" class="tg-df-card-cta ${isSavingsSquadMode ? 'tg-df-cta-savings-squad' : ''}" style="text-decoration: none;">${ctaText}${_a}                  ${_div}                ${_div}              ${_div}            `;                        return htmlOutput;            } catch (e) {               console.log("Error rendering deal in map for index", index, typeof deal === 'object' ? JSON.stringify(deal) : deal, "MSG:", e.message);               return '';            }          }).join('');                    if (displayDeals.length > this.displayLimit || ((this.getViewMode() === 'carousel' || this.getViewMode() === 'auto') && displayDeals.length > 0 && displayDeals.length % ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12) === 0)) {            if (this.getViewMode() === 'carousel') {               dealsHtml += `                 \x3Cbutton type="button" class="tg-df-load-more-card tg-df-load-more">                   \x3Csvg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 8px;">\x3Cpath d="M5 12h14">\x3C/path>\x3Cpath d="m12 5 7 7-7 7">\x3C/path>\x3C/svg>                   Load More                 ${_button}               `;            } else {               dealsHtml += `                 \x3Cdiv style="width: 100%; display: flex; justify-content: center; margin-top: 16px; grid-column: 1 / -1;">                   \x3Cbutton type="button" class="tg-df-tag-outline tg-df-load-more" style="padding: 8px 24px; border-radius: 100px; font-weight: 600; font-size: 14px; cursor: pointer; display: flex; align-items: center;">Load More${_button}                 ${_div}               `;            }          }                    this.grid.innerHTML = dealsHtml;                    let gridWrapper = this.grid.parentElement;          if (gridWrapper && gridWrapper.classList.contains('tg-df-grid-wrapper')) {             let existingChevron = gridWrapper.querySelector('.tg-df-carousel-scroll-right');             if (this.getViewMode() === 'carousel') {                 if (!existingChevron) {                     gridWrapper.insertAdjacentHTML('beforeend', '\n                 \x3Cbutton class="tg-df-carousel-scroll-right" type="button" aria-label="Scroll right" onclick="this.previousElementSibling.scrollBy({left: 200, behavior: \'smooth\'})">\x3Csvg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">\x3Cpath d="m9 18 6-6-6-6">\x3C/path>\x3C/svg>\x3C/button>');                 }             } else {                 if (existingChevron) {                     existingChevron.remove();                 }             }          }                    const loadMoreBtn = this.grid.querySelector('.tg-df-load-more');          if (loadMoreBtn) {            loadMoreBtn.addEventListener('click', async () => {              if (typeof trackElementInteraction === 'function') {                trackElementInteraction({ id: 'load-more', name: 'Load more', label: 'Load More Results' });              }              if (displayDeals.length <= this.displayLimit) {                 loadMoreBtn.innerHTML = `                  <svg class="tg-df-spinner" style="width: 16px; height: 16px; display: inline-block; vertical-align: middle; margin-right: 8px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83"/></svg>                  Loading...                 `;                 loadMoreBtn.disabled = true;                 await this.fetchDeals(this.currentQuery, true);              } else {                 this.displayLimit += ((this.rowsSelect && this.rowsSelect.value) ? parseInt(this.rowsSelect.value, 10) : 12);                 this.render();              }            });          }                      this.bindCouponButtons();            this.checkAndUpdateCoupons();                        // Allow hawklinks.js to discover and rewrite our widget links             // by appending the .article-body class and manually triggering processArticle.            let container = this.root.classList.contains('tg-df-container') ? this.root : this.root.querySelector('.tg-df-container');            if (container && !container.classList.contains('article-body')) {               container.classList.add('article-body');            }            setTimeout(() => {               if (this.grid && !this.grid.classList.contains('article-body')) this.grid.classList.add('article-body');            if (!this.processArticleFired) {                  this.processArticleFired = true;                  document.dispatchEvent(new CustomEvent('processArticle', { detail: { element: this.root } }));               }            }, 50);          } catch(e) {            console.warn("Widget render error", e);          }        }                async checkAndUpdateCoupons() {          const wrappers = Array.from(this.root.querySelectorAll('.tg-df-coupon-wrapper'));          if (wrappers.length === 0) return;                    const merchants = [...new Set(wrappers.map(w => w.getAttribute('data-merchant')).filter(Boolean))];          if (merchants.length === 0) return;          const couponResultsMap = await this.checkMerchantsCouponsBulk(merchants);                    for (const merchant of merchants) {            const hasCoupons = !!couponResultsMap[merchant];            const merchantWrappers = wrappers.filter(w => w.getAttribute('data-merchant') === merchant);            merchantWrappers.forEach(wrapper => {              const spinner = wrapper.querySelector('.tg-df-coupon-spinner');              const btn = wrapper.querySelector('.tg-df-tag-coupons');                            if (spinner) spinner.style.display = 'none';                            if (hasCoupons && btn) {                btn.style.display = 'inline-flex';              } else if (!hasCoupons) {                wrapper.style.display = 'none';              }            });          }        }        updateFloatingCopyBar() {          if (!this.editorBar || !this.editorSelectedCount) return;          if (this.editorMode && this.selectedDeals.size > 0) {            this.editorBar.style.display = 'flex';            this.editorSelectedCount.innerText = this.selectedDeals.size;          } else {            this.editorBar.style.display = 'none';          }        }        async copySelectedDealsToCMS() {           function htmlToSlate(htmlString) {              if (!htmlString) return [{ type: 'paragraph', children: [{ text: '' }] }];              let doc;              if (typeof window !== 'undefined' && window.DOMParser) {                 doc = new DOMParser().parseFromString(htmlString, 'text/html');              } else {                 doc = document.implementation.createHTMLDocument('');                 doc.body.innerHTML = htmlString;              }                            function parseNode(node, marks = {}) {                  if (node.nodeType === 3) {                      const text = node.textContent;                      if (!text) return null;                      return { text: text, ...marks };                  }                  if (node.nodeType === 1) {                      const tagName = node.tagName.toLowerCase();                      if (tagName === 'br') {                          return { type: 'line-break', children: [{ text: '' }] };                      }                      if (tagName === 'p') {                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return { type: 'paragraph', children };                      }                      if (tagName === 'strong' || tagName === 'b') {                          const newMarks = { ...marks, bold: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'em' || tagName === 'i') {                          const newMarks = { ...marks, italic: true };                          return Array.from(node.childNodes).map(child => parseNode(child, newMarks)).flat().filter(Boolean);                      }                      if (tagName === 'a') {                          const href = node.getAttribute('href') || '';                          let children = Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                          if (children.length === 0) children.push({ text: "" });                          return {                              type: 'link',                              url: href,                              isNoFollow: (node.getAttribute('rel') || '').includes('nofollow'),                              isSponsored: (node.getAttribute('rel') || '').includes('sponsored'),                              isOpenNewTab: node.getAttribute('target') === '_blank',                              isPreventDataRewrite: false,                              children: children                          };                      }                      return Array.from(node.childNodes).map(child => parseNode(child, marks)).flat().filter(Boolean);                  }                  return null;              }                            let blocksArray = [];              let currentParagraphChildren = [];              function flushParagraph() {                  if (currentParagraphChildren.length > 0) {                      blocksArray.push({ type: 'paragraph', children: currentParagraphChildren });                      currentParagraphChildren = [];                  }              }              Array.from(doc.body.childNodes).forEach(node => {                  const parsed = parseNode(node, {});                  const parsedItems = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : []);                  parsedItems.forEach(item => {                      if (item.type === 'paragraph') {                          flushParagraph();                          blocksArray.push(item);                      } else {                          currentParagraphChildren.push(item);                      }                  });              });              flushParagraph();              if (blocksArray.length === 0) {                  blocksArray = [{ type: 'paragraph', children: [{ text: '' }] }];              }              return blocksArray;           }           const blocks = [];                      this.editorCopyBtn.innerHTML = '\x3Cspan class="tg-df-coupon-spinner" style="display:inline-block; margin-right:8px; border-top-color:#fff;">' + '<' + '/span> Copying...';           for (const deal of Array.from(this.selectedDeals.values())) {              const url = deal.url;              const merchant = deal.merchant;              const title = deal.title;              const image = deal.image;              const currentPrice = deal.currency + deal.rawPrice;              const wasPrice = deal.hasWasPrice && deal.rawMsrp > deal.rawPrice ? deal.currency + deal.rawMsrp : '';                            let couponsChildren = [];              try {                  const area = this.getAreaCode();                  const apiUrl = new URL('https://search-api.fie.future.net.uk/widget.php');                  apiUrl.searchParams.append('model_name', 'Everything');                  apiUrl.searchParams.append('language', 'en-GB');                  apiUrl.searchParams.append('area', area);                  apiUrl.searchParams.append('combine_product_types', '1');                  apiUrl.searchParams.append('filter_merchant_name', merchant);                  apiUrl.searchParams.append('all_filters', 'false');                  apiUrl.searchParams.append('exclude_unlabelled', 'false');                  apiUrl.searchParams.append('include_specs', 'false');                  apiUrl.searchParams.append('sort', 'voucher');                  apiUrl.searchParams.append('distinct_merchants', 'natural');                  apiUrl.searchParams.append('filter_product_types', 'vouchers,offer_deals,newsletter');                  apiUrl.searchParams.append('rows', '3');                  apiUrl.searchParams.append('origin', 'widgets-clientside');                                    let res; try { res = await fetch(apiUrl.toString()); } catch (e) { return; }                  if (res.ok) {                      const data = await res.json();                      let offers = [];                      if (data && data.widget && data.widget.data && Array.isArray(data.widget.data.offers)) {                        offers = data.widget.data.offers;                      } else if (data && data.data && Array.isArray(data.data.offers)) {                        offers = data.data.offers;                      }                                            if (offers.length > 0) {                          couponsChildren.push({ text: "Also check out these coupons: ", bold: true });                          offers.slice(0, 3).forEach((offer, idx) => {                              const actualOffer = offer.offer || offer;                              const offerName = actualOffer.name || actualOffer.title || offer.model_name || offer.title || offer.name || 'Coupon';                              const linkUrl = actualOffer.link || actualOffer.url || actualOffer.offer_link || '#';                              couponsChildren.push({ type: "line-break", children: [{ text: "" }] });                              couponsChildren.push({ text: "🎟️ " });                              couponsChildren.push({                                  type: "link",                                  url: linkUrl,                                  isNoFollow: true,                                  isSponsored: false,                                  isOpenNewTab: true,                                  isPreventDataRewrite: false,                                  children: [{ text: offerName, bold: true }]                              });                          });                      }                  }              } catch (err) {                  console.warn('Failed to fetch coupons for', merchant, err);              }              let descriptionValue = [];              if (deal.text) {                 descriptionValue = htmlToSlate(deal.text);              } else {                 const dealDescriptions = [                   `Don't miss out on this fantastic deal for the ${title}. It is currently available at ${merchant} for a highly competitive price.`,                   `We've spotted an excellent price drop on the ${title}. Grab it now at ${merchant} before it's gone.`,                   `The ${title} is currently seeing a generous discount over at ${merchant}. This is a perfect time to buy if you've been holding out.`,                   `If you're in the market for the ${title}, ${merchant} has just the deal for you.`,                   `Score the ${title} for less at ${merchant} right now. This is a rare chance to save big.`,                   `Upgrade your setup with the ${title}, now available at a stellar price via ${merchant}.`                 ];                 const randomDescription = dealDescriptions[Math.floor(Math.random() * dealDescriptions.length)];                 descriptionValue = [                    { type: "paragraph", children: [{ text: randomDescription }] }                 ];              }                            if (couponsChildren.length > 0) {                 let lastBlock = descriptionValue[descriptionValue.length - 1];                 if (lastBlock && lastBlock.type === 'paragraph') {                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children.push({ text: "Also check out these coupons: ", bold: true });                     lastBlock.children.push({ type: "line-break", children: [{ text: "" }] });                     lastBlock.children = lastBlock.children.concat(couponsChildren);                 } else {                     descriptionValue.push({                         type: "paragraph",                         children: [                             { type: "line-break", children: [{ text: "" }] },                             { type: "line-break", children: [{ text: "" }] },                             { text: "Also check out these coupons: ", bold: true },                             { type: "line-break", children: [{ text: "" }] },                             ...couponsChildren                         ]                     });                 }              }              function normalizeCurrencyToISO(symbol) {                const map = { '£': 'GBP', '$': 'USD', 'A$': 'AUD', 'CA$': 'CAD', '€': 'EUR' };                return map[symbol] || symbol;              }              const isoCurrency = normalizeCurrencyToISO(deal.currency);              blocks.push({                 id: (window.crypto && window.crypto.randomUUID) ? window.crypto.randomUUID() : 'cms-' + Date.now() + Math.random(),                 blockTypeName: "deal",                 excludeFrom: [],                 collapsible: false,                 props: {                    description: {                       value: descriptionValue,                       touched: false,                       validationMessage: ""                    },                    image: {                       value: {                          credit: [{ type: "paragraph", children: [{ text: merchant }] }],                          dateCreated: Date.now(),                          dateModified: Date.now(),                          distribution: [],                          fileSize: 0,                          height: 1000,                          id: deal.id,                          imageRights: "",                          src: image,                          name: title + ".jpg",                          tags: [],                          width: 1000                       },                       touched: false,                       validationMessage: ""                    },                    showDealButton: { value: true, touched: false, validationMessage: "" },                    isPreferredPartner: { value: false, touched: false, validationMessage: "" },                    linkHref: { value: url, touched: false, validationMessage: "" },                    linkLabel: { value: "", touched: false, validationMessage: "" },                    linkIsNoFollow: { value: true, touched: false, validationMessage: "" },                    linkIsSponsored: { value: false, touched: false, validationMessage: "" },                    linkIsOpenNewWindow: { value: true, touched: false, validationMessage: "" },                    customPromoFlags: { value: [], touched: false, validationMessage: "" },                    showStarDeal: { value: false, touched: false, validationMessage: "" },                    savingType: { value: "none", touched: false, validationMessage: "" },                    starDealPromoFlag: { value: "", touched: false, validationMessage: "" },                    showEditorsChoice: { value: false, touched: false, validationMessage: "" },                    editorsChoiceTitle: { value: "", touched: false, validationMessage: "" },                    hawkPriceCurrency: { value: { value: isoCurrency, label: isoCurrency }, touched: false, validationMessage: "" },                    hawkPrice: { value: deal.hasWasPrice ? String(deal.rawMsrp) : String(deal.rawPrice), touched: false, validationMessage: "" },                    hawkSalePrice: { value: String(deal.rawPrice), touched: false, validationMessage: "" },                    lastCheckedPriceDate: { value: "", touched: false, validationMessage: "" },                    hawkModel: { touched: false, validationMessage: "" },                    productId: { value: "", touched: false, validationMessage: "" },                    voucherId: { value: "", touched: false, validationMessage: "" },                    brand: { value: deal.brand || merchant, touched: false, validationMessage: "" },                    productName: { value: title, touched: false, validationMessage: "" },                    label: { value: "", touched: false, validationMessage: "" },                    retailer: { value: merchant, touched: false, validationMessage: "" },                    priceCheckError: false                 },                 failedFetchError: ""              });           }           const payload = {              type: "articleBuilderPages",              data: blocks           };           const jsonStr = JSON.stringify(payload);                      if (navigator.clipboard && navigator.clipboard.writeText) {              navigator.clipboard.writeText(jsonStr).then(() => {                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              }).catch(err => {                 console.warn('Failed to copy text: ', err);                 alert('Failed to copy deals to clipboard. See console.');              });           } else {              // Fallback              const textArea = document.createElement("textarea");              textArea.value = jsonStr;              document.body.appendChild(textArea);              textArea.focus();              textArea.select();              try {                 document.execCommand('copy');                 this.editorCopyBtn.innerHTML = 'Copied!';                 setTimeout(() => {                    this.editorCopyBtn.innerHTML = '\x3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">\x3Crect x="9" y="9" width="13" height="13" rx="2" ry="2"><' + '/rect>\x3Cpath d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"><' + '/path><' + '/svg> Copy to CMS';                 }, 2000);              } catch (err) {                 console.warn('Fallback: Oops, unable to copy', err);                 alert('Fallback: Failed to copy deals to clipboard.');              }              document.body.removeChild(textArea);           }        }      }      // Initialize the Widget      if (document.readyState === 'loading') {        document.addEventListener('DOMContentLoaded', () => new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer }));      } else {        new DealsFinderWidget({ rootId: 'signal-deals-finder-root', rootNode: shadowRoot, hostContainer: hostContainer });      }    })();  </script></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve taken over 6,000 photos on the Canon EOS R6 Mark III — here are my favorite portraits, wildlife, and landscape shots ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/my-favorite-shots-taken-on-the-canon-eos-r6-mark-iii</link>
                                                                            <description>
                            <![CDATA[ I've spent the last two weeks testing the Canon EOS R6 Mark III and after taking 6,000 photos on it, I'm seriously impressed. Here are my favorite photos. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">3xX9vUv68arSi9pjv5rUNQ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/zNesjStttmk4QB2xjTa87L-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 20 Jun 2026 07:15:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Mirrorless Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/zNesjStttmk4QB2xjTa87L-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A person holding a Canon EOS R6 Mark III and a photo of goslings taken on said camera]]></media:description>                                                            <media:text><![CDATA[A person holding a Canon EOS R6 Mark III and a photo of goslings taken on said camera]]></media:text>
                                <media:title type="plain"><![CDATA[A person holding a Canon EOS R6 Mark III and a photo of goslings taken on said camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/zNesjStttmk4QB2xjTa87L-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>When I'm not writing 10,000 words a week, I can usually be found playing video games, at a concert, planning my next tattoo... or out on a walk with a camera in hand. I've loved photography for as long as I can remember, and I'm very lucky that I get to bring my love of photography to my job.</p><p>As a reviews writer at Tom's Guide, I test the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a>, and last year alone, <a href="https://www.tomsguide.com/cameras-photography/best-photos-i-ve-taken-on-cameras-reviewed-2025">I reviewed nearly 30 cameras</a>! And I'm showing no signs of stopping this year. For the last two weeks, I've been putting the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">Canon EOS R6 Mark III</a> through its paces, and now that I've finished testing it, I can confirm that it's a phenomenal mid-range shooter.</p><div class="product"><a data-dimension112="f24b0fc7-1201-4a63-8b48-550864f05206" data-action="Deal Block" data-label="Good becomes great as the Canon EOS R6 Mark III introduces substantial upgrades to its predecessor. A 32.5MP sensor, 7K Open Gate video, a full-size HDMI port, and CFExpress Type-B support make the EOS R6 Mark III a formidable mid-range camera. Images are detailed, and colors appear true to life. Best-in-class autofocus and IBIS give you very little to complain about. 40fps drive is the cherry on top for wildlife and sports photographers." data-dimension48="Good becomes great as the Canon EOS R6 Mark III introduces substantial upgrades to its predecessor. A 32.5MP sensor, 7K Open Gate video, a full-size HDMI port, and CFExpress Type-B support make the EOS R6 Mark III a formidable mid-range camera. Images are detailed, and colors appear true to life. Best-in-class autofocus and IBIS give you very little to complain about. 40fps drive is the cherry on top for wildlife and sports photographers." data-dimension25="$2799" href="https://www.adorama.com/canon-eos-r6-mark-iii-mirrorless-camera-body-only/p/car6m3" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1134px;"><p class="vanilla-image-block" style="padding-top:79.72%;"><img id="nkH3zjAzUbcGaNCu5kaAy3" name="Screenshot 2026-06-16 at 14.19.31" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/nkH3zjAzUbcGaNCu5kaAy3.png" mos="" align="middle" fullscreen="" width="1134" height="904" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Good becomes great as the Canon EOS R6 Mark III introduces substantial upgrades to its predecessor. A 32.5MP sensor, 7K Open Gate video, a full-size HDMI port, and CFExpress Type-B support make the EOS R6 Mark III a formidable mid-range camera. Images are detailed, and colors appear true to life. Best-in-class autofocus and IBIS give you very little to complain about. 40fps drive is the cherry on top for wildlife and sports photographers.<a class="view-deal button" href="https://www.adorama.com/canon-eos-r6-mark-iii-mirrorless-camera-body-only/p/car6m3" target="_blank" rel="nofollow" data-dimension112="f24b0fc7-1201-4a63-8b48-550864f05206" data-action="Deal Block" data-label="Good becomes great as the Canon EOS R6 Mark III introduces substantial upgrades to its predecessor. A 32.5MP sensor, 7K Open Gate video, a full-size HDMI port, and CFExpress Type-B support make the EOS R6 Mark III a formidable mid-range camera. Images are detailed, and colors appear true to life. Best-in-class autofocus and IBIS give you very little to complain about. 40fps drive is the cherry on top for wildlife and sports photographers." data-dimension48="Good becomes great as the Canon EOS R6 Mark III introduces substantial upgrades to its predecessor. A 32.5MP sensor, 7K Open Gate video, a full-size HDMI port, and CFExpress Type-B support make the EOS R6 Mark III a formidable mid-range camera. Images are detailed, and colors appear true to life. Best-in-class autofocus and IBIS give you very little to complain about. 40fps drive is the cherry on top for wildlife and sports photographers." data-dimension25="$2799">View Deal</a></p></div><p>Once I'd finished testing the EOS R6 Mark III, I took a look at how many photos I'd taken on it — and there were 6,569 stills for me to sift through. Many of them were throwaways or part of burst sequences (of which I only edited and finalized a few). I've been thoroughly impressed and delighted with the photos I've taken, so I thought I'd share my favorites with you. So please, enjoy!</p><h2 class="article-body__section" id="section-wildlife"><span>Wildlife</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Gyjer5UkV9bN6zDAxCcHDL.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/tYamDB6GJykdRkv6xTS9nK.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nEJzFTNmemEmowki8i5fBK.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/cg62KN8FRgytspmndy4EqH.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/8PAbDBc9dkNQiaoETj3yfH.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/rTwiypuDVrZn93wktJeGfG.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Lv52Tk3777ban7MWXnwTpF.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Me8oCeR29isPAZobxUZLuG.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Some of my favorite subjects to photograph are animals and birds, and the Canon EOS R6 Mark III is adept at wildlife photography. What I love about the EOS R6 Mark III is its rapid autofocus system featuring no fewer than a whopping 6,097 selectable points. In my testing, I found the EOS R6 Mark III was quick to lock onto the subjects I was photographing. It didn't falter even once.</p><p>All the images in the gallery above were captured with the AF set to Auto, and the camera was quick to recognize the subjects as birds, so it switched to Animal Detection.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/a72m8Lfi6MZpBWXXs4ZhAJ.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/chSbJjwcLN3FrhM6naFSxH.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VHYRdKJR2YWjUrkNHvqsoH.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2KGM9ufRrbTQZf6Q5gTyfH.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gBMPCTLGBPc4ZAUbjUWQPG.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/o5ihpTX4au5gnxiDY9AwPL.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ex252Ssxii2Vua99tE3KaG.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>When I wanted to capture birds in flight or in action, I took full advantage of the camera's 40fps sequential shooting or high-speed drive. The EOS R6 Mark III is capable of capturing 330 JPEGs and 150 RAW images with its electronic shutter. 40fps drive ensures you never miss a moment of action, and 0.5 seconds of pre-burst capture records 20 frames when you half-press the shutter button.</p><p>As soon as I spotted the two pairs of moorhens fighting with each other, I pointed the camera in their direction, and it locked onto them. I then fired off a burst (while '<a href="https://www.youtube.com/watch?v=Bt4D8oA3mxU" target="_blank" rel="nofollow">Kung Fu Fighting</a>' by Carl Douglas played in my head) and captured hundreds of images of the birds attacking one another with their feet and beaks. You can see a few of the sequential images in the gallery above.</p><h2 class="article-body__section" id="section-portraits"><span>Portraits</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/dtNSdK2Fddey93oxbABDse.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LCLQrdLpcnfDhcj59F3qYe.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/o7rxxireMyibVeReL9W9Qc.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Alongside being a fast camera, the Canon EOS R6 Mark III is great for portraits, whether those are taken indoors or outdoors. The 32.5MP CMOS sensor captures plenty of detail, and there's enough room for making tight crops in post-production. Human subject detection and tracking is fantastic, and if the camera can't detect any eyes, it automatically and seamlessly switches to face and body detection, so the EOS R6 Mark III is very reliable.</p><p>There are a few in-camera filters to play with too, such as Vivid, Warm, Cool, and Monochrome, to name a few. The black and white color profile is adept at capturing dramatic portraits. I took a couple of photos of my colleagues in the Monochrome filter, as you can see above. Also, thanks to up to 8.5-stops of in-body image stabilization (IBIS), you can confidently shoot at slow shutter speeds when lighting isn't adequate.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/vDpMPYRapknZfeexVKnE4e.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Y9jMowHMspcnqbgziGJBpc.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/rWdWDs3PN6QNhYdUvYDk9e.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The portraits I captured outdoors turned out just as lovely, even when the lighting was unpredictable and uncontrollable (unlike in a controlled studio environment). I adore the photos of my partner in the gallery above. The colors have come out beautifully, and I love the different shades of gray in the first slide.</p><h2 class="article-body__section" id="section-landscapes"><span>Landscapes</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/DSoUfZksMxAPRDi3Exd9rE.jpg" alt="A landscape photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nvbhfEYnu7WZjgRJCEvneB.jpg" alt="A landscape photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/yGH9EQMNkVrfpd5jwWjP2C.jpg" alt="A landscape photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/cTYUoifqSYT22TNr9wLUbC.jpg" alt="A landscape photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/33f7M7wGBRqXPxbCqCd3aC.jpg" alt="A landscape photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/aDBrgtZueUPR95mkhtX9MB.jpg" alt="A landscape photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/WchSCdBe5gdcM45dmuVajB.jpg" alt="A landscape photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The Canon EOS R6 Mark III is truly a jack of all trades, and if you pair it with a wide-angle lens, like the <a href="https://www.bhphotovideo.com/c/product/1947744-REG/canon_rf_14mm_f_1_4_l.html" target="_blank" rel="nofollow">Canon RF 14mm f/1.4 L VCM</a>, you've got a winning combination for landscape photography. You can capture a lot of the scene, as I did in the photos above.</p><p>Another feature that makes the EOS R6 Mark III an outstanding landscape snapper is its excellent dynamic range. The camera excels at retaining both shadow and highlight detail. This gives you more wiggle room in post-production as you can lift the shadows to reveal detail, and recover highlights by pulling them down. The result is a balanced image where highlights don't appear blown out, and dark areas remain noise-free.</p><p>The EOS R6 Mark III is fully weather-sealed too, and it's protected against dust and moisture, so you can shoot confidently in changing environmental conditions. As someone who lives in the U.K., where it can start raining out of nowhere, you have no idea what a boon this is.</p><h2 class="article-body__section" id="section-details"><span>Details</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/JRBdm9sveRvc59k48WyckN.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/m3k6CEYHNxn6Xd6cCF7a6N.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iJFBhtLfoVKpXbE82gvcUP.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EppgwsQ6y8HtA5SP7iMb2P.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I'll be the first to admit that I'm not a massive fan of macro photography, but I've enjoyed capturing close-ups with the Canon EOS R6 Mark III. Well, the photos in the gallery above aren't exactly macros or <em>close </em>close-ups, but they're shots I've used to test the camera's ability to capture the finer details.</p><p>Thanks to the camera's 32.5MP CMOS full-frame sensor, the images are packed with detail, intricate textures, crisp edges, and fine gradations. As I mentioned earlier, you can crop a fair amount too to really highlight your subject — as I did with the photos of the bees on the flowers. The soft pastel shades of the flowers look gorgeous, too.</p><h2 class="article-body__section" id="section-to-sum-up"><span>To sum up...</span></h2><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="NBToc3miMi6Z8DrsSYjon3" name="Canon_EOS_R6_Mark_III-24" alt="A woman holding a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/NBToc3miMi6Z8DrsSYjon3.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>There's a lot more I can say about the camera, which is what my full, in-depth <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">Canon EOS R6 Mark III review</a> is there for, of course. For <a href="https://www.adorama.com/canon-eos-r6-mark-iii-mirrorless-camera-body-only/p/car6m3" target="_blank" rel="nofollow">$2,799 at Adorama</a> / <a href="https://www.lcegroup.co.uk/New/Canon-EOS-R6-Mark-III-Full-Frame-Mirrorless-Digital-Camera-Body_214336.html" target="_blank" rel="nofollow">£2,799 at London Camera Exchange</a>, you're getting a camera that pulls out all the stops. 7K Open Gate video, CFExpress Type-B support, a 32.5MP sensor for beautiful photos, advanced autofocus, 8.5-stops of IBIS, and Canon's vaunted color science — all of these come together to deliver a powerful mid-range package.</p><p>Testing the EOS R6 Mark III has been a lot of fun, and if you're on the hunt for a camera that doesn't compromise on image quality and doesn't break the bank, few are better than this. Have you tried the EOS R6 Mark III yet, or would you like to? Let me know what you think in the comments below!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ey62qW"></div>                            </div>                            <script src="https://kwizly.com/embed/ey62qW.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">I’ve taken over 6,000 photos on the Canon EOS R6 Mark III — and this feature-packed camera is nothing short of a mid-range masterpiece</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-just-tested-the-canon-eos-r5-mark-ii-to-photograph-wildlife-and-this-one-feature-has-changed-how-i-take-photos-forever">I tested the Canon EOS R5 Mark II to photograph wildlife — and this one feature has changed how I take photos forever</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-wildlife-camera-first-impressions">‘Few wildlife cameras come close to this’: why the OM System OM-1 Mark II is perfect for photographing birds in flight and other animals</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve taken over 6,000 photos on the Canon EOS R6 Mark III — and this feature-packed camera is nothing short of a mid-range masterpiece ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review</link>
                                                                            <description>
                            <![CDATA[ The Canon EOS R6 Mark III is an excellent mid-range camera, boasting a 32.5MP sensor for detailed, beautiful photos, 7K Open Gate video, and 40fps drive. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">JkGnQ4N6WhNQ5A5mVW2Hff</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/m9xM7ZtdzeHgjHnwzwY3SR-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 17 Jun 2026 11:24:40 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Mirrorless Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/m9xM7ZtdzeHgjHnwzwY3SR-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A Canon EOS R6 Mark III mirrorless camera]]></media:description>                                                            <media:text><![CDATA[A Canon EOS R6 Mark III mirrorless camera]]></media:text>
                                <media:title type="plain"><![CDATA[A Canon EOS R6 Mark III mirrorless camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/m9xM7ZtdzeHgjHnwzwY3SR-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Canon is no stranger to making some of the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a> out there, and the EOS R6 Mark III is one of the finest mid-range shooters you can buy today. Sitting comfortably between enthusiast and pro-grade cameras, it improves on its predecessor by introducing a 32.5MP sensor, 7K Open Gate video, and CFExpress Type-B support. It retains 40fps burst shooting for wildlife and sports photographers, but brings with it more advanced tracking which powers its excellent autofocus system.</p><p>On paper, the EOS R6 Mark III sounds like a standout choice for practically any genre of photography. In practice, it’s even more impressive. It’s responsive, dependable, and it delivers sharp, vibrant images. And even though the price has crept up and the camera’s overheating limitations may be frustrating for some, these shortcomings do little to overshadow what is otherwise a well-rounded camera in its class.</p><p>To find out whether you should buy it, and how I got on during the two weeks I spent with it, read my full Canon EOS R6 Mark III review.</p><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-specs"><span>Canon EOS R6 Mark III review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.adorama.com/canon-eos-r6-mark-iii-mirrorless-camera-body-only/p/car6m3" target="_blank" rel="nofollow">$2,799</a> / <a href="https://www.lcegroup.co.uk/New/Canon-EOS-R6-Mark-III-Full-Frame-Mirrorless-Digital-Camera-Body_214336.html" target="_blank" rel="nofollow">£2,799</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>32.5MP CMOS full frame</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>DIGIC X</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>5-axis IBIS, up to 8.5 stops</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Dual Pixel CMOS AF II</p></td></tr><tr><td class="firstcol " ><p><strong>Focus modes</strong></p></td><td  ><p>One Shot, Servo AF, AI Focus AF, Manual focus</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>0.5-inch OLED EVF, approx. 3.69 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3-inch TFT LCD touchscreen, approx. 1.6 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>ISO range</strong></p></td><td  ><p>100-102,400</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>7K/30fps Open Gate | 7K/60fps RAW | 4K/120fps | FHD/180fps</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>USB-C, HDMI Type-A, 3.5mm mic in, 3.5mm headphones out, 2.5mm remote control, 1x CFExpress Type-B, 1x SD/SDHC/SDXC UHS-II</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless/bluetooth</strong></p></td><td  ><p>Wi-Fi, Bluetooth</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>40fps (electronic) | 12fps (mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Max shutter speed</strong></p></td><td  ><p>30s to 1/16,000s (stills, electronic) | 30s to 1/8,000s (stills, mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>620 shots (LCD) | 390 shots (EVF)</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>5.44 x 3.87 x 3.48 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>1.54lbs (with battery and cards)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-price-availability"><span>Canon EOS R6 Mark 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6cJFpQAV3BXZubqNTziWxQ" name="Canon_EOS_R6_Mark_III-16" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/6cJFpQAV3BXZubqNTziWxQ.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>The Canon EOS R6 Mark III, as its name suggests, is the third iteration of the EOS R6 and the third model in the lineup — and there’s a fourth, if you count the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/the-canon-eos-r6-v-and-the-sony-a7r-vi-dropped-this-week-both-look-incredible-but-heres-the-one-im-most-excited-for-and-why">Canon EOS R6 V.</a> The EOS R6 Mark III succeeds the three-year-old EOS R6 Mark II and is a little pricier than its predecessor ($300 precisely).</p><p>Its body is available for <a href="https://www.adorama.com/canon-eos-r6-mark-iii-mirrorless-camera-body-only/p/car6m3" target="_blank" rel="nofollow">$2,799 at Adorama</a> / <a href="https://www.lcegroup.co.uk/New/Canon-EOS-R6-Mark-III-Full-Frame-Mirrorless-Digital-Camera-Body_214336.html" target="_blank" rel="nofollow">£2,799 at London Camera Exchange</a>. There are two kits available too: one with the RF 24-105mm f/4-7.1 IS STM lens for <a href="https://www.adorama.com/canon-eos-r6-mark-iii-mirrorless-camera-24-105-mm-lens/p/car6m3k1" target="_blank" rel="nofollow">$3,149</a> / <a href="https://www.lcegroup.co.uk/New/Canon-EOS-R6-Mark-III-with-RF-24-105mm-F4-7.1-IS-STM-Lens_214337.html" target="_blank" rel="nofollow">£3,149</a>, and another with the RF 24-105mm F4L IS USM lens for <a href="https://www.adorama.com/canon-eos-r6-mark-iii-rf-24-105-mm-f-4-l-is-usm/p/car6m3k" target="_blank" rel="nofollow">$4,049</a> / <a href="https://www.lcegroup.co.uk/New/Canon-EOS-R6-Mark-III-with-RF-24-105mm-F4L-IS-USM-Lens_214338.html" target="_blank" rel="nofollow">£3,899</a>.</p><p>Given this price and the camera’s features, the EOS R6 Mark III finds itself competing against the likes of the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-spent-a-day-shooting-on-the-new-sony-a7-v-here-are-my-first-impressions-spoiler-alert-its-great">Sony A7 V</a> ($2,899) and the <a href="https://www.tomsguide.com/reviews/fujifilm-xh2s">Fujifilm X-H2S</a> ($2,499), with both cameras boasting fast high-speed drives: 30fps and 40fps respectively.</p><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-design"><span>Canon EOS R6 Mark 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Xk44oeL3fXUGktGbuJZJ4R" name="Canon_EOS_R6_Mark_III-16x9" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/Xk44oeL3fXUGktGbuJZJ4R.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>If you put the Canon EOS R6 Mark III next to its predecessor, you’d hardly be able to differentiate between the two. The EOS R6 Mark III features an almost identical physical body design, sculpted grip, and control layout. But if something ain’t broke, don’t fix it, and the EOS R6 Mark III is wonderful to handle.</p><p>Its protruding grip is spacious enough to wrap your fingers around and even use the camera one-handed comfortably — if you’ve got medium-sized hands like I do, at least. Dimensions remain fairly identical too, with the EOS R6 Mark III measuring 5.44 x 3.87 x 3.48 inches and weighs 1.54lbs with battery and memory cards. It doesn’t feel bulky or too heavy to carry around my neck or in my backpack.</p><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="CkQV4giPYL2BWrHWv5it5b" name="Canon_EOS_R6_Mark_III-25" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/CkQV4giPYL2BWrHWv5it5b.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>The camera’s overall handling will naturally be affected by the lens you’re using. I tested the Canon RF 28-70mm F2L USM and the Canon RF 14mm F1.4L VCM lenses with it, and the former on its own weighs nearly 3.2lbs. The kit, then, weighs approximately 4.7lbs which sounds like a lot, but thanks to great weight distribution and the EOS R6 Mark III’s sculpted grip, I didn’t find it too unwieldy to handle.</p><h2 id="viewfinder-2">Viewfinder</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="m9xM7ZtdzeHgjHnwzwY3SR" name="Canon_EOS_R6_Mark_III-24" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/m9xM7ZtdzeHgjHnwzwY3SR.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>The EOS R6 Mark III’s 0.5-inch OLED electronic viewfinder (EVF) is spacious and comfortable to look through, and it’s plenty bright too. It has a resolution of approximately 3.69 million dots so you get a sharp, detailed and life-like representation of the scene you’re capturing, and this, in turn, helps you accurately frame and capture your subject.</p><p>This is the same resolution as the Sony A7 V and the <a href="https://www.tomsguide.com/reviews/panasonic-lumix-g9-ii">Panasonic Lumix G9II</a> ($1,899), but it’s a little lower than the similarly priced <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-review">OM System OM-1 Mark II</a>’s 5.76 million dots ($2,399). If you want a higher-res EVF, you’ll have to splurge on the likes of the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II</a> ($6,499) or the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/the-canon-eos-r6-v-and-the-sony-a7r-vi-dropped-this-week-both-look-incredible-but-heres-the-one-im-most-excited-for-and-why">Sony A7R VI </a>($4,499), both of which pack 9.44 million dots each in their EVFs.</p><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="qhPBR9Wx3uCko86Gt2Vc7b" name="Canon_EOS_R6_Mark_III" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/qhPBR9Wx3uCko86Gt2Vc7b.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>One thing I love about the EOS R6 Mark III’s viewfinder is its 120Hz refresh rate — which is the same as the OM System OM-1 Mark II and the pricier <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r5-mark-ii-review">Canon EOS R5 Mark II</a> ($4,299). The fast refresh rate ensures subjects you’re tracking remain free of motion blur, and the camera delivers smooth and realistic motion — great for fast-paced wildlife or sports photography.</p><p>It’s important to note that the EOS R6 Mark III doesn’t feature blackout-free shooting. That means in the EVF you get short bits of black screens between shots. They’re hardly noticeable by the naked eye, especially when shooting at 40fps, although this will obviously be an issue for sports or wildlife photographers, but those people should be looking at a high speed blackout-free camera like the Sony A1 II or A7R VI instead. Or, if you can sacrifice sensor real estate, the OM System OM-1 Mark II.</p><h2 id="monitor">Monitor</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3xRFW54QaDZUq739e5opCR" name="Canon_EOS_R6_Mark_III-8" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/3xRFW54QaDZUq739e5opCR.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>The EOS R6 Mark III’s rear panel houses the 3-inch TFT LCD touchscreen which has a resolution of approximately 1.6 million dots. This is the same resolution as that on the OM System OM-1 Mark II and the Fujifilm X-H2S. It’s a little lower than on the Sony A7 V (2.09M) and the Canon EOS R5 Mark II (2.1M), but it’s still bright enough that I haven’t had trouble reading the screen even in direct sunlight.</p><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="NnY5jmrA2tWcNSN94YctEb" name="Canon_EOS_R6_Mark_III-9" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/NnY5jmrA2tWcNSN94YctEb.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>In my testing, I found the EOS R6 Mark III’s touchscreen highly responsive as all of my taps and swipes were registered instantly. The vari-angle mechanism also meant that I could shoot at awkward angles with ease, like when I wanted to take a low-angle photo of a duck loafing on the ground, or of birds flying right overhead.</p><h2 id="ports-2">Ports</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="HvrbY78SNA48X6xWAajkXb" name="Canon_EOS_R6_Mark_III-14" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/HvrbY78SNA48X6xWAajkXb.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>Canon hasn’t spared any expense when it comes to the EOS R6 Mark III’s ports, and the camera features better connectivity when compared to its predecessor. You’ll find the battery compartment on the bottom plate, and this houses an upgraded LP-E6P battery pack. Located next to this is a standard tripod mount thread.</p><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="fdnrkd5F3w2f2PzsP4LCGb" name="Canon_EOS_R6_Mark_III-13" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/fdnrkd5F3w2f2PzsP4LCGb.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>All other ports are located on the camera’s left-hand edge, and they’re protected by dust- and water-resistant covers. The biggest upgrade, in terms of connectivity, is the presence of a full-size HDMI Type-A port on the EOS R6 Mark III (versus Micro HDMI Type-D on the EOS R6 Mark II). Next to the HDMI port is a USB-C 3.2 Gen 2 for charging, data transfer, and 4K/60fps UVC live-streaming functionality (a first for an EOS body). And as this is a hybrid stills and video camera, there are two 3.5mm jacks for a mic as well as headphones, and a 2.5mm remote control terminal.</p><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="gRpxw8dXD7ceEmtdxefpBb" name="Canon_EOS_R6_Mark_III-12" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/gRpxw8dXD7ceEmtdxefpBb.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>Two memory card slots are located on the EOS R6 Mark III’s right-hand edge. While the EOS R6 Mark II featured dual UHS-II SD card slots, its successor has one SD/SDHC/SDXC UHS-II card slot and one CFExpress Type-B slot, supporting up to 8TB. The latter is there to match the camera’s higher resolution, accommodate 7K RAW files, and sustain the 40fps drive.</p><h2 id="weather-sealing-2">Weather-sealing</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uVaqCnqvWgwdDvvHQvVD6R" name="Canon_EOS_R6_Mark_III-2" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/uVaqCnqvWgwdDvvHQvVD6R.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>The EOS R6 Mark III features virtually identical weather-sealing to the EOS R6 Mark II — which means it’s resistant against sand, dust, moisture and rain. You should be able to use the camera in challenging conditions comfortably. I’ve tested it in light rain and haven’t had to worry about water getting inside or damaging the monitor.</p><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-controls"><span>Canon EOS R6 Mark III review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="q2sCm2s7gcDGLFqFD6od3R" name="Canon_EOS_R6_Mark_III-5" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/q2sCm2s7gcDGLFqFD6od3R.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>As I alluded to earlier, the Canon EOS R6 Mark III’s control layout is fairly similar to that of its predecessor’s. Though it’s cheaper, I’ve found the EOS R6 Mark III as easy to use (and get to grips with) as the pricier EOS R5 Mark II. All the buttons and dials are well-placed and within easy reach. There’s also a Lock switch located between the On and Off button which prevents accidental settings changes.</p><p>Many of the EOS R6 Mark III’s buttons are customizable via the settings menu. For those who photograph different genres, there are three Custom Shooting Modes — C1, C2 and C3 — located on the mode dial. You can tailor settings, such as autofocus modes, button layouts, and exposure parameters, and save entire custom setups to these.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="cLeMoUxJJXnr7VM3eKbE7R" name="Canon_EOS_R6_Mark_III-4" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/cLeMoUxJJXnr7VM3eKbE7R.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>I also like how thoughtfully-placed the customizable buttons are. For instance, the Rate button located in the top-left corner of the rear panel can be fully customized, and I found it easy to press with my left thumb. There’s a Multi-Function (M-Fn) button near the shutter button on the top plate which I customized to swap between drive modes.</p><p>The EOS R6 Mark III’s menu system is, as per, a joy to use. I’ve always enjoyed how well-designed Canon’s color-coded menus are, with everything easily understandable even by those who may be picking up a Canon for the first time.</p><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-autofocus"><span>Canon EOS R6 Mark III review: Autofocus</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="DpSHePeULAHubN5YBv7wNb" name="Canon_EOS_R6_Mark_III-18" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/DpSHePeULAHubN5YBv7wNb.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><div  class="fancy-box"><div class="fancy_box-title">Lenses used for testing</div><div class="fancy_box_body"><p class="fancy-box__body-text"><ul><li><a data-analytics-id="inline-link" href="https://www.adorama.com/canon-rf-50-mm-f-1-4-l-vcm-lens/p/car5014" target="_blank" rel="nofollow"><em>Canon RF 14mm F1.4L VCM</em></a></li><li><a data-analytics-id="inline-link" href="https://www.adorama.com/canon-rf-28-70-mm-f-2-l-usm-lens/p/car28702" target="_blank" rel="nofollow"><em>Canon RF 28-70mm F2L USM</em></a></li></ul></p></div></div><p>The EOS R6 Mark III utilizes Canon’s Dual Pixel CMOS AF II autofocus system to detect and lock on to humans, animals/birds, and vehicles. The EOS R6 Mark III features no fewer than 6,097 selectable points, which is a hell of a lot more than the Sony A7 V’s 759 points and the Fujifilm X-H2S’s 425 points.</p><p>Regardless of the subject I was photographing, I found the EOS R6 Mark III quick to focus. Similar to the Sony A1 II and Canon EOS R5 Mark II, the EOS R6 Mark III features Auto subject-detection, so when that’s selected, the camera automatically switches between human, animal and vehicle detection.</p><p>I found this extremely useful when going from photographing birds at the park to my partner, and then randomly jumping to a bicycle. Not once did the EOS R6 Mark III fail to detect the subject. If you want the camera to do even more thinking for you, you can toggle AI Focus, which is an intelligent autofocus setting that automatically changes between One Shot (for still subjects) and Servo (for tracking motion). This is handy for when you’re photographing birds and they suddenly fly away, as an example.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/dtNSdK2Fddey93oxbABDse.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hjHP3WZ6jz9GrAZTvZreHc.jpg" alt="A photo of a person on a bridge taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/vDpMPYRapknZfeexVKnE4e.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The photos in the gallery above were taken with the EOS R6 Mark III’s autofocus set to human detection. I love that the camera automatically switches between Eye, Face, Head and Body detection, thanks to its deep learning AF system. It searches for the eyes first and if it can’t find one, it seamlessly switches to finding other body parts. I appreciated not having to swap between these detection settings every time.</p><p>Indoors, in a photo studio with bright lights, the camera was quick to lock onto my colleague’s eyes, as you can see in the first slide. Outdoors, I was impressed by the EOS R6 Mark III detecting and locking onto the person holding the umbrella, even though you can’t see their face and their body is obscured by the bridge’s railings. Similarly, it was quick to focus on my partner, too.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/nAG6RvbtKEKxjRgL3joZf3.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9jeq9DsysSiwncP4APSiY3.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2nGDPsqkguJRPCRB8DchR4.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Animal/bird detection works just as well if not better. The EOS R6 Mark III quickly locked onto the swan in the first slide in the gallery above, and I could see on the monitor that it was focusing on the eyes — even though they aren’t that obvious. In the second shot, even though the moorhen chick was far away and small, the EOS R6 Mark III didn’t struggle to find it, and likewise, it was adept at focusing on the group of ducklings huddled together in the third shot.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3606px;"><p class="vanilla-image-block" style="padding-top:66.67%;"><img id="ex252Ssxii2Vua99tE3KaG" name="Canon_EOS_R6_Mark_III-sample-22" alt="A photo of a bird taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/ex252Ssxii2Vua99tE3KaG.jpg" mos="" align="middle" fullscreen="" width="3606" height="2404" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>I’ve enjoyed using the EOS R6 Mark III’s focus joystick too, as the omission of which I always miss when testing cameras like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-q3-43-review">Leica Q3 43</a> ($7,380). I’ve used it extensively to quickly reset focus points, and even used it to focus on a specific subject in a crowded area, like in the photo of the pigeon drinking water (above).</p><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-stabilization"><span>Canon EOS R6 Mark III review: Stabilization</span></h2><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="o5Hy7WazvX3qyVcRkysnSR" name="Canon_EOS_R6_Mark_III-22" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/o5Hy7WazvX3qyVcRkysnSR.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>Depending on the lens you use with it, the Canon EOS R6 Mark III is capable of delivering up to 8.5-stops of in-body image stabilization (IBIS). This means that you can shoot at slow shutter speeds handheld and in low-light conditions without worrying about blur from camera shake.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/wFEwUebMnc6ToMNjbDfUdP.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption>Taken at 1s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/74QTsYa4jXUgqJAGe3mKvP.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption>Taken at 2s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RF 28-70mm F2L USM lens I tested doesn’t feature any stabilization (bizarre, I know) so for the two photos above, I relied entirely on the EOS R6 Mark III’s IBIS. The first image was shot at 1 second and the second at 2 seconds. As you can see, the first shot is clear and stable while the second has been impacted by camera shake — and I expected that, given the weight of the lens. But this is great as it shows the camera’s stabilization works excellently.</p><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:66.65%;"><img id="nHRN35Ex8cvffNt2qtXhrN" name="4s — EOSR6III" alt="A photo taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/nHRN35Ex8cvffNt2qtXhrN.jpg" mos="" align="middle" fullscreen="" width="2048" height="1365" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Taken at 4s. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>I then took a few photos at varying shutter speeds with the RF 14mm F1.4L VCM which, again, doesn’t feature an image stabilizer. But because the lens is much lighter than the RF 28-70mm F2L USM, my hands didn’t feel as unstable and I was able to shoot at even slower shutter speeds. The image above was captured at 4 seconds!</p><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-image-quality"><span>Canon EOS R6 Mark III review: Image quality</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/o5ihpTX4au5gnxiDY9AwPL.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JRBdm9sveRvc59k48WyckN.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/m3k6CEYHNxn6Xd6cCF7a6N.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/sxkC6zYnQxNRjHLypAzsJK.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iJFBhtLfoVKpXbE82gvcUP.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EppgwsQ6y8HtA5SP7iMb2P.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/HtNherKjLVUG3iZeZcsfkL.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/cTYUoifqSYT22TNr9wLUbC.jpg" alt="A landscape photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/cg62KN8FRgytspmndy4EqH.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Gyjer5UkV9bN6zDAxCcHDL.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/aDBrgtZueUPR95mkhtX9MB.jpg" alt="A landscape photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I’m yet to test a mirrorless camera from Canon that doesn’t produce stunning images. The EOS R6 Mark III is an imaging powerhouse, packing a 32.5MP full frame CMOS sensor. Boasting Canon’s vaunted color science, color reproduction is faithful and true to life, and different shades and hues look beautiful. You can see a few sample images in the gallery above. All the colors are well-balanced with none appearing oversaturated or light on contrast.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/TkMTFGXEvQ5Q7VnCKTomXe.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/AARD5J2dSBRyHiCrmkLAWe.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hmu8NF3kvKCPogiHvaKQYe.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iYvay9DJyNoanZkmYWtcYe.jpg" alt="A photo taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>While the EOS R6 Mark III doesn’t feature a sensor as high-res as, say, the Canon EOS R5 Mark II with its 45MP Stacked CMOS or the Sony A7R VI with its 66.8MP Stacked CMOS, you still get enough resolution to create crops.</p><p>The first image in the gallery above is an extreme crop of the second, and the third of the fourth. That’s as far as I could crop in without losing detail. In the portrait of the pigeon, you can still make out its individual neck feathers, and you can still see the different skin texture in the photo of my partner.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/rWdWDs3PN6QNhYdUvYDk9e.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/rTwiypuDVrZn93wktJeGfG.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LCLQrdLpcnfDhcj59F3qYe.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/o7rxxireMyibVeReL9W9Qc.jpg" alt="A portrait of a woman taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Like many other cameras from Fujifilm and Sony, the EOS R6 Mark III comes with built-in picture styles and filters, such as Vivid, Monochrome, Cool, Warm, and so on. In the gallery above, the first photo was taken with Vivid, the second with Cool, and the third and fourth in Monochrome.</p><p>I love how the greens pop and look bright in the first shot, and the green water in the second shot appears muted and cinematic. Meanwhile, the shadows look deep and highlights look bright (but not too bright) in the grayscale images, making the Monochrome filter great for portraits.</p><h2 id="high-speed-drive-2">High-speed drive</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/gBMPCTLGBPc4ZAUbjUWQPG.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/vM727LKuoCniKvddZwZTKK.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/qRCGEFnanBSe7mrCedUpx3.jpg" alt="A photo of pigeons in flight taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/yYyuEAzxQkdRmCawgCQjV3.jpg" alt="A photo of pigeons in flight taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The EOS R6 Mark III utilizes Canon’s DIGIC X processor for 40fps sequential shooting with the electronic shutter, and the camera captures 330 JPEGs and 150 RAW images in this mode. Having 40fps drive at your disposal means you can capture every single moment of action, which makes the EOS R6 Mark III a strong choice for wildlife and sports. If you want an even higher drive, the OM System OM-1 Mark II boasts 120fps blackout-free sequential shooting, but you’d be sacrificing resolution for that.</p><p>I found 40fps to be more than sufficient for capturing birds in action, as you can see in the gallery above. With the camera set to High-speed Continuous Shooting+ via the Drive quick menu, I was able to photograph the pigeon landing on the stump, and the crow frantically hopping about with a berry in its mouth. The electronic shutter also has a shutter speed of 30s to 1/16,000s, giving you plenty of speed to capture blur-free images.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/2KGM9ufRrbTQZf6Q5gTyfH.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/a72m8Lfi6MZpBWXXs4ZhAJ.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/chSbJjwcLN3FrhM6naFSxH.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VHYRdKJR2YWjUrkNHvqsoH.jpg" alt="A photo of a bird taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Thanks to the 40fps burst and best-in-class autofocus, I was able to capture a series of photos featuring two pairs of moorhens fighting with each other, and you can see four shots of that in the gallery above. I was limited by the RF 28-70mm F2L USM’s longest focal length which prevented me from getting really close-up shots, so imagine what you could do with a telephoto lens, like the <a href="https://www.bhphotovideo.com/c/product/1793388-REG/canon_rf_200_800mm_f_6_3_9_is.html" target="_blank" rel="nofollow">RF 200-800mm F6.3-9 IS USM</a> ($2,249).</p><p>Similar to many other high-speed Canon cameras, the EOS R6 Mark III supports pre-burst capture up to 0.5 seconds, and unlike its predecessor, you don’t need to enable the RAW Burst mode to do so. The EOS R6 Mark III records 20 frames when you half-press the shutter button and these are saved to your memory card when the button is fully pressed — handy for never missing a split-second moment.</p><h2 id="iso-performance-2">ISO performance</h2><p>The EOS R6 Mark III boasts good high ISO performance. It has a base ISO of 100 and it tops out at 102,400, just like the Sony A1 II, the Sony A7R VI, and the Canon EOS R5 Mark II. To see how it fared, I put the EOS R6 Mark III through an extreme ISO test indoors.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/MfjwihNM9hBc8AQL2ZtNgN.jpg" alt="A photo of scales taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iaHmgzKLEq7ADKHmcEfEgP.jpg" alt="A photo of scales taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Images come out as you’d expect at ISO6400 so I’ll shift focus to high ISO values instead. The images above were shot at 12,800: the first is a JPEG export of the CR3 RAW file, and the second is the straight-out-of-camera (SOOC) JPEG. As you can see, the EOS R6 Mark III’s in-camera noise reduction has done well to reduce the noise or grain in the SOOC JPEG. There’s no loss in sharpness either as the legends on the scale are still discernible.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/S2bftGDrEQAhcdTH7dQtkN.jpg" alt="A photo of scales taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/xeC7KYYMLKTpUEA9WeVabP.jpg" alt="A photo of scales taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I then upped the ISO to 25,600. There’s a lot of noise in the JPEG export of the CR3 file (first slide above) but once again, the camera has done well to smoothing it out in the SOOC JPEG (second slide above). The blue background does appear a bit grainy but it’s far from ruinous. Again, there’s no drop in sharpness. This was also the case with the EOS R5 Mark II and the A1 II.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/DMV5DjRcfLE9ExMqMnDJ4P.jpg" alt="A photo of scales taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4JQmgBMHe8S28wqaBYn7bN.jpg" alt="A photo of scales taken on a Canon EOS R6 Mark III" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Finally, ISO 51,200, which is the limit I’d recommend pushing the EOS R6 Mark II — as a last resort, that is. As you can see in the SOOC JPEG (second slide above), the plant in the background has lost the sharpness around its leaves and it doesn’t look as detailed anymore. It has a watercolor effect which we’ve seen on micro four-thirds cameras, like the OM System OM-1 Mark II.</p><h2 id="dynamic-range-2">Dynamic range</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1365px;"><p class="vanilla-image-block" style="padding-top:150.04%;"><img id="6SQsnAmWipZMa5rm6RHKDi" name="DR Canon_EOS_R6_Mark_III-sample" alt="A photo taken on a Canon EOS R6 Mark III to test the dynamic range performance" src="https://cdn.mos.cms.futurecdn.net/6SQsnAmWipZMa5rm6RHKDi.jpg" mos="" align="middle" fullscreen="" width="1365" height="2048" 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 EOS R6 Mark III offers up to 14+ stops of dynamic range at base ISO100, so in theory, you can make extensive adjustments in post without negatively impacting image quality. To test this, I took a couple of photos outdoors and lifted the shadows and reduced the highlights in Adobe Photoshop Lightroom.</p><p>In the backlit photo of my partner above (the original of which you saw earlier), the camera’s done a great job of maintaining highlight detail while also exposing for the comparatively dim unlit face.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/7KNrPUVpRoigfPeXm9xYHi.jpg" alt="A photo taken on a Canon EOS R6 Mark III to test the dynamic range performance" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/KuqLJJX7yyTowzFayntHMi.jpg" alt="A photo taken on a Canon EOS R6 Mark III to test the dynamic range performance" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The EOS R6 Mark III keeps shadows relatively noise-free too. In the gallery above, I reduced the highlights and lifted the shadows and doing so revealed many leaves that I couldn’t see in the first unedited shot.</p><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-video"><span>Canon EOS R6 Mark III review: Video</span></h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/UUxJDRmV1g0" allowfullscreen></iframe></div></div><p>The Canon EOS R6 Mark III doesn’t skimp on video either. It’s a powerful hybrid camera capable of recording 7K/30fps Open Gate video where it utilizes its entire uncropped sensor and resolution, as well as 7K/60fps RAW video. The presence of a full-size port means you can shoot 7K RAW ProRes via HDMI too. Naturally, you can record 4K video at 120fps too. You can see a sample video, shot in 4K at 60fps, above. The video looks smooth and plenty of detail has been captured. The colors look lovely, too.</p><p>Alongside these resolutions and framerates, the EOS R6 Mark III is capable of recording in Canon Log 2 and Canon Log 3, where the latter captures a wide dynamic range of up to 13.3+ stops. C-Log is usually reserved for Canon’s Cinema lineup, so it’s good to see it on the EOS R6 Mark III as well as the EOS R5 Mark II mirrorless cameras too.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="sf8G6qznjraiCXg6tTyuTb" name="Canon_EOS_R6_Mark_III-26" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/sf8G6qznjraiCXg6tTyuTb.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>While the EOS R6 Mark III boasts solid video specs, it doesn’t feature a built-in cooling fan or system, so it can get very hot, and in turn, it doesn’t last very long. Canon says that the camera can record 7K/60fps RAW for only 23 minutes at 73°F / 23°C. This isn’t great news for hybrid shooters, but this is why the Canon EOS R6 V exists. The EOS R6 Mark III’s video-first sibling features an active cooling fan and airflow vents to prevent shutdowns caused by overheating (and it even packs the same 32.5MP sensor).</p><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-battery-life"><span>Canon EOS R6 Mark 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="XYkQUEuKMVR3dP5q4sQYbb" name="Canon_EOS_R6_Mark_III-15" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/XYkQUEuKMVR3dP5q4sQYbb.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>Like I said before, the Canon EOS R6 Mark III introduces and utilizes a new and upgraded LP-E6P battery pack. It’s also compatible with LP-E6NH and LP-E6N packs but not with the original LP-E6. The EOS R6 Mark III is CIPA-rated for 620 shots when using the LCD monitor or 390 shots when using the EVF.</p><p>Of course, CIPA-ratings are generally conservative as they’re determined using strict and usually single-shot testing parameters, so more often than not, your camera will last you longer in the field. In my testing, I took nearly 2,500 photos before charging the camera. Your mileage will heavily depend on your shooting settings and modes.</p><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="rARx5LRxqi94CrGzUDPH2b" name="Canon_EOS_R6_Mark_III-7" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/rARx5LRxqi94CrGzUDPH2b.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>For the purposes of comparison, we’ll use the 620-shot LCD CIPA-rating. This is slightly lower than the Sony A7R VI’s 670-shot, the Sony A7 V’s 750-shot, and the Fujifilm X-H2S’ 720-shot battery life. The EOS R6 Mark III upstages the OM System OM-1 Mark II, though, which is rated for 520 shots. But like I said, the EOS R6 Mark III has lasted me comfortably for a full day’s worth of shooting, so you should get by just fine.</p><p>As for video, while the EOS R6 Mark III is capable of shooting 7K/30fps RAW and Open Gate, it’s worth noting that it doesn’t feature an active built-in fan or cooling system, so it has a tendency to overheat, as it did in my testing after about 20 minutes. It’s disappointing that the EOS R6 Mark III doesn’t cope well once it gets too hot (sounds like me during a heatwave), but like I said, that’s one of the reasons why the EOS R6 V, the lineup’s video-first camera, exists.</p><h2 class="article-body__section" id="section-canon-eos-r6-mark-iii-review-verdict"><span>Canon EOS R6 Mark 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:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ZqE9CEWLs7EWrHGE48gn4b" name="Canon_EOS_R6_Mark_III-20" alt="A Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/ZqE9CEWLs7EWrHGE48gn4b.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>Canon’s EOS R6 lineup hasn’t failed to impress us so far. The EOS R6 Mark III delivers a genuinely well-rounded shooting experience. It feels at home capturing wildlife, sports, landscapes, studio portraits, travel, what-have-you. The combination of a sharp 32.5MP CMOS sensor, class-leading autofocus and stabilization, and 40fps burst shooting make the EOS R6 Mark III a remarkable creative tool. Throw Canon’s beautiful color science, intuitive controls and ergonomics into the mix and you’ve got a camera that’s easy to enjoy.</p><p>There are a few caveats, though. While the camera boasts 7K video capabilities, videographers will likely run into the camera’s thermal limits sooner than they’d like. Some rivals offer longer battery lives too.</p><p>But these compromises feel relatively minor when weighed against everything the EOS R6 Mark III offers. For photographers seeking a fast, versatile and dependable camera, this is one of the strongest options available today — and a worthy successor to the EOS R6 Mark II.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Insta360's gimbal camera has just hit shelves, and there's one factor that has me lining up to purchase one stat ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/insta360s-gimbal-camera-has-just-hit-shelves-and-theres-one-factor-that-has-me-lining-up-to-purchase-one-stat</link>
                                                                            <description>
                            <![CDATA[ The gimbal camera landscape has a new contender, but the Insta360 Luna Ultra is about more than just matching its rivals’ specs. Here's why. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">qhBp95bBRbzvXVV45GWpfT</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/YMtxquVtSQESnshcDeGJmk-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 16 Jun 2026 01:28:57 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ lucy.scotting@futurenet.com (Lucy Scotting) ]]></author>                    <dc:creator><![CDATA[ Lucy Scotting ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/UC9ncAYxkmJ5ipHEyX44ri.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Lucy Scotting is a staff writer for Tom’s Guide Australia, primarily covering lifestyle, streaming and internet-related news. Lucy started her career writing for HR and staffing industry publications, with articles covering emerging tech, business and finance.&lt;/p&gt;&lt;p&gt;In her spare time, Lucy can be found watching sci-fi movies, working on her dystopian fiction novel (in progress since 2017) or hanging out with her dog, Fletcher. If she’s not found doing any of the above, she’s likely on her next adventure to a new city, country, or continent. &lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/YMtxquVtSQESnshcDeGJmk-1280-80.jpg">
                                                            <media:credit><![CDATA[Insta360]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Insta360 lifestyle image]]></media:description>                                                            <media:text><![CDATA[Insta360 lifestyle image]]></media:text>
                                <media:title type="plain"><![CDATA[Insta360 lifestyle image]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/YMtxquVtSQESnshcDeGJmk-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As a tech reviewer who also creates content, I rely heavily on my iPhone 17 Pro Max's camera capabilities when taking product photos and creating Instagram reels. That said, the latest camera in Insta360's arsenal has me reaching for my wallet — and it's not because it's a <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review">DJI Osmo Pocket 4</a> dupe. </p><p>The <a href="https://www.amazon.com.au/dp/B0GXJRJ2MK" target="_blank">Insta360 Luna Ultra</a> has been highly anticipated since its sneak peek at CES 2026, and it officially launched last week on June 11. However, in its short time on the market, the camera and its maker have already experienced a slate of criticism, including <a href="https://www.digitalcameraworld.com/cameras/video-cameras/what-insta360-and-djis-new-court-battle-means-for-the-luna-ultra-and-the-osmo-pocket-4p-cameras" target="_blank">a legal battle instigated by DJI</a>, but that doesn't mean the new gimbal should be dismissed by potential buyers completely. </p><p>In fact, it should be commended for the new features that make it a direct competitor to DJI's longstanding reign over gimbal cameras. While its lightweight, handheld compact design is, admittedly, <em>very </em>similar to its competitor, the Luna Ultra introduces a first-of-its-kind feature in the form of a completely detachable 2-inch OLED touchscreen for remote controlling of the device. </p><p>Gone are the days when creators (and photography newbs like myself) had to coordinate solo shots by using a timer, an app, or, in my case, using my Apple Watch to control the camera. Now, you can just as easily set up the Luna Ultra, take a few steps back (up to 20 metres) and still have total control over what the camera is filming (even if the subject is you), as you'll have the screen in your hands.</p><p>Better yet, the Luna Ultra also comes with a dual-camera system that can capture in 8K resolution, boasts Leica-powered Summicron lenses and filters, a 3-axis stabilisation system, precise subject tracking and up to 12x zoom, including 6x lossless zoom. </p><p>The only factor that might deter potential buyers a little is cost. The Luna Ultra is now available in Australia at select retailers, including the<a href="https://store.insta360.com/au/product/luna-series" target="_blank"> Insta360 Store</a>,<a href="https://www.amazon.com.au/dp/B0GXJRJ2MK" target="_blank"> Amazon</a> and <a href="https://www.jbhifi.com.au/products/insta360-luna-ultra-8k-gimbal-camera-standard-bundle-cosmic-black" target="_blank">JB Hi-Fi</a>, for AU$1,229.99, in Cosmic Black and Stellar White. You can also purchase a <a href="https://www.amazon.com.au/Insta360-Luna-Ultra-Touchscreen-Photography/dp/B0GY8BS2FS" target="_blank">creator bundle with additional accessories for AU$1,549.99</a>.</p><h2 id="levelling-up-content-creation">Levelling up content creation</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:9504px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fdTjcNWDgnh2SA8yhHHd4n" name="6 (1) copy" alt="Insta360 lifestyle image" src="https://cdn.mos.cms.futurecdn.net/fdTjcNWDgnh2SA8yhHHd4n.jpg" mos="" align="middle" fullscreen="" width="9504" height="5346" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Insta360)</span></figcaption></figure><p>Content creation — whether it's rooted in a serious or playful nature — is challenging, so having the right tools is becoming more and more important. In my case, 'content creation' means putting together a fun highlight reel of my life, but for a plethora of other creators, it's <em>literally </em>their bread and butter. </p><p>The Insta360 Luna Ultra and other gimbal cameras have taken plenty of hassle out of the work that is content creation, and by no means is the social media boom going to slow down anytime soon. Even if there's a potential issue of patent infringement, there's no denying that whether you purchase the DJI Pocket Osmo 4 or the Insta360 Luna Ultra (or another gimbal device completely), your photos and videos will be vastly better than those taken on any type of smartphone. </p><p>I, for one, am eager to get my hands on the Luna Ultra, largely to check out what it can do to level up my social videos, as well as to take product pictures and footage for my reviews. While its pricing may be a hard pill to swallow, especially for those with tighter purse strings, sometimes investing in a passion project pays off in strides. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I've got an an action-packed summer planned — here are 6 rugged DJI cameras built to survive it ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/entertainment/ive-got-an-an-action-packed-summer-planned-here-are-6-rugged-dji-cameras-built-to-survive-it</link>
                                                                            <description>
                            <![CDATA[ If you're planning an adventurous summer then you'll want to take a camera that can withstand a bit of wear and tear, while still capturing great footage. These six DJI cameras are just the thing... ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">7NL5hMFutNqj72LQNX6NYY</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/SRvQGtUfdcgaG7Bd3WoQcj-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 15 Jun 2026 14:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[GoPros &amp; Action Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></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/SRvQGtUfdcgaG7Bd3WoQcj-1280-80.jpg">
                                                            <media:credit><![CDATA[DJI / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of DJI cameras.]]></media:description>                                                            <media:text><![CDATA[A selection of DJI cameras.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of DJI cameras.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/SRvQGtUfdcgaG7Bd3WoQcj-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I'm planning a few outdoor adventures this summer. I'll be doing some travelling, fitting in a few long hikes, and a lot of trail running. As I'm going to be out in the countryside, I'll also be recording the footage. That's always a risk with a regular smartphone, though. Not only is it an expensive device, it's one with my whole digital life on it and I don't want to risk breaking it.</p><p>That's where an action camera comes in handy, like this selection from DJI. These six cameras are tough and rugged enough to sustain wear and tear, while still shooting exceptional footage. They're ideal for popping in a bag and taking on a trip with you and they're also great for shooting sports. We've picked out six of our favorites below and included a couple of drones so you can take to the skies, too. If you're planning on an active summer, take a look at our picks below.</p>        <div class="featured_product_block featured_block_versus" data-id="68cc4560-cd97-4e6e-873a-6b1833e7a1fc">            <a href="https://www.amazon.com/dp/B0DS2B3P2B" data-model-name="DJI Osmo Action 4 Essential Combo" 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/NHagaxCYPeqdyLFDrpyQRf.jpg" alt="DJI Osmo Action 4 Essential Combo"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                            <div class='featured__brand'>BRAND</div>                    <div class="featured__title">DJI Osmo Action 4 Essential Combo</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="d814b285-8bd9-45a9-8f53-98ed30be7c42">            <a href="https://www.amazon.com/dp/B0C783J9WC" data-model-name="DJI Osmo Action 4 Camera with Batteries, Extension Rod, and Battery Case" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/KqEFoD9WrDgem4QeSVW4qh.jpg" alt="Dji Osmo Action 4 Adventure Combo, Waterproof Action Camera With 1/1.3" Sensor, 4k/120fps Video, Stunning Low-Light, 8h Extended Battery Life With 3 Batteries, 1.5m Extension Rod, Battery Case"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>DJI</div>                                        <div class="featured__title">DJI Osmo Action 4 Camera with Batteries, Extension Rod, and Battery Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="125c0643-1a24-4ad9-b05f-605ed549cce6">            <a href="https://www.amazon.com/dp/B07FTRYVT5" data-model-name="DJI Neo Fly More Mini Drone with 4K UHD Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/teSLHAZftcitd7VNFLpmoi.jpg" alt="Dji Neo Fly More Combo, Mini Drone With 4k Uhd Camera for Adults, 135g Self Flying Drone That Follows You, Palm Takeoff, Subject Tracking, Quickshots, Stabilized Video, With Rc-N3 and 3 Batteries"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>DJI</div>                                        <div class="featured__title">DJI Neo Fly More Mini Drone with 4K UHD Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="603dee79-51f8-407f-92df-7bce2d536d76">            <a href="https://www.amazon.com/dp/B0DBR8HVZG" data-model-name="Osmo Action 5 Pro with Extension Rod and Battery Case" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/P6CimdoyXQ6vMvdXYipyKh.jpg" alt="Dji Osmo Action 5 Pro Adventure Combo, Waterproof Action Camera With 1/1.3" Sensor, 4k/120fps Video, Subject Tracking, 12h Extended Battery Life With 3 Batteries, 1.5m Extension Rod, Battery Case"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>DJI</div>                                        <div class="featured__title">Osmo Action 5 Pro with Extension Rod and Battery Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="0f4010af-49e8-4264-9241-dd703884a93b">            <a href="https://www.amazon.com/dp/B0FNMPZ1RS" data-model-name="DJI Osmo Action 6 Camera with Batteries, Extension Rod, and Battery Case" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/EJY54Hirzr7jZZmZTeMoRh.jpg" alt="Dji Osmo Action 6 Enhanced Combo, 8k Waterproof Action Camera With 1/1.1" Square Sensor, Variable Aperture of F/2.0–f/4.0, Extended Battery Life With 2 Batteries, 1.5m Extension Rod, Battery Case"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>DJI</div>                                        <div class="featured__title">DJI Osmo Action 6 Camera with Batteries, Extension Rod, and Battery Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="6e8668ac-e1a9-494e-a6d8-0dba892e4377">            <a href="https://www.amazon.com/dp/B07FTJ8RXY" data-model-name="DJI Flip Drone with 4K UHD Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/MpVefhnXMjfGNguNJhxTmj.jpg" alt="Dji Flip With Rc 2 Screen Remote Controller, Follow Me Drone With 4k Uhd Camera for Adults, Under 249 G, 3-Axis Gimbal Stabilization, 44000ft/13km Video Transmission, Palm Takeoff, 31-Min Flight Time"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>DJI</div>                                        <div class="featured__title">DJI Flip Drone with 4K UHD Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The $101 Yashica Tank could be the perfect way to hop on the digi-cam trend — I tested it on vacation to see what it can do ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/yashica-tank-review</link>
                                                                            <description>
                            <![CDATA[ The Yashica Tank is a cheap compact digi-cam that’s perfect for throwing in your bag, but the budget price may be holding it back. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">5zvoypBfUg4BMMCj4xAsrQ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ggtx8tnkXbDZzSpj4xfN2F-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 11 Jun 2026 16:47:15 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></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/ggtx8tnkXbDZzSpj4xfN2F-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Yashica Tank in pink photographed in front of a blue background]]></media:description>                                                            <media:text><![CDATA[Yashica Tank in pink photographed in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[Yashica Tank in pink photographed in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ggtx8tnkXbDZzSpj4xfN2F-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I’m a sucker for a cheap compact camera, and on the surface, the Yashica Tank ticks all the boxes. It’s fun, cute and easy to use, so could it be the <a href="https://www.tomsguide.com/us/best-cheap-cameras,review-6319.html">best cheap camera</a> for you? It’s a good option for anyone who wants a portable camera for taking quick snaps with friends without breaking the bank on a camera like the viral <a href="https://www.tomsguide.com/cameras-photography/canon-powershot-g7-x-mark-iii-still-worth-it-in-2026">Canon PowerShot G7 X Mark III</a>. Of course, you shouldn’t expect image quality like an expensive compact: it has more of a Y2K vibe. It also has a 180º flip screen to easily take selfies, and the controls are simple. </p><p>But it feels a little flimsy due to the low price point, and the lack of optical zoom means that if you want to zoom in, you have to sacrifice image resolution. The switch at the top of the camera for video and photos only lets you playback one format at a time, which is annoying, and occasionally, the shutter response is delayed. </p><p>That said, I still had a lot of fun with this camera, so to find out more about it, keep reading my full Yashica Tank review.</p><h2 class="article-body__section" id="section-yashica-tank-review-specs"><span>Yashica Tank review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.yashica.com/product-page/yashica-tank-compact-digital-camera-pink-marshmallow?currency=USD" target="_blank" rel="nofollow">$101</a> / <a href="https://www.yashica.com/product-page/yashica-tank-compact-digital-camera-black?currency=GBP" target="_blank" rel="nofollow">£75</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>12MP 1/2.8-inch CMOS</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Electronic only (EIS) </p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Active/Contrast-Detection AF</p></td></tr><tr><td class="firstcol " ><p><strong>Focus modes</strong></p></td><td  ><p>AF Assist Lamp</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3-inch flip screen, 16:9 LCD, 360 x 640</p></td></tr><tr><td class="firstcol " ><p><strong>ISO range</strong></p></td><td  ><p>100 - 6400</p></td></tr><tr><td class="firstcol " ><p><strong>Digital zoom</strong></p></td><td  ><p>8x</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>4K 30fps</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>USB-C, 1x microSD</p></td></tr><tr><td class="firstcol " ><p><strong>Max shutter speed</strong></p></td><td  ><p>1/125 sec</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>220 photos (800mAh)</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>3.9 x 2.2 x 0.1 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>3.7 ounces</p></td></tr><tr><td class="firstcol " ><p><strong>Colors</strong></p></td><td  ><p>Pink Marshmallow, Sky Blue, Black, Brown</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-yashica-tank-review-price-availability"><span>Yashica Tank review: Price & availability</span></h2><p>The Yashica Tank is available for purchase directly from the Yashica online store for <a href="https://www.yashica.com/product-page/yashica-tank-compact-digital-camera-pink-marshmallow?currency=USD" target="_blank" rel="nofollow">$101</a> / <a href="https://www.yashica.com/product-page/yashica-tank-compact-digital-camera-black?currency=GBP" target="_blank" rel="nofollow">£75</a>. It’s available in four different colors, including Pink Marshmallow, Sky Blue, Black and Brown. </p><p>The camera very much resembles the <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-c1-compact-camera-review">Kodak PIXPRO C1</a>, which costs $99. This camera has the same tiny style, a 12MP sensor, and you can use built-in digital filters, like on the Yashica. But if you want something for a similar price with superior zoom capabilities, you should check out the <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-fz45-review">Kodak PIXPRO FZ45</a> ($99). This camera has optical zoom and digital zoom, whereas the Tank only has digital zoom, which decreases the quality of the image when zoomed in. </p><p>If you’re looking for a hobby camera to throw in your bag for taking fun pictures with friends, the Tank is fine. But, if you want something that can actually take <em>good</em> photos, then you’d need to spend more money and look at a camera like the <a href="https://www.tomsguide.com/cameras-photography/panasonic-lumix-zs99-tz99-review">Lumix ZS99</a> ($499). The ZS99 has more resolution, a 30x optical zoom to get great zoomed-in shots, and is still compact enough to take with you everywhere. </p><p>Or if you need something for adventurous trips that can withstand a little roughhousing, then check out the rugged 16MP <a href="https://www.tomsguide.com/cameras-photography/pentax-wg-1000-compact-camera">Pentax WG-1000</a> ($196). It can withstand water submersion and drops from height. With that said, the Tank is good for hobby photography with friends.</p><h2 class="article-body__section" id="section-yashica-tank-review-design"><span>Yashica Tank review: Design</span></h2><p>You can’t deny it, the Yashica Tank is incredibly cute. It’s a compact camera, so, as that moniker implies, it’s very small. In fact, it’s smaller than my <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-17-review">iPhone 17</a>, measuring just 3.9 x 2.2 x 0.1 inches. This meant I could throw it in my bag and shove it in my pocket without worrying about it taking up too much space. It weighs just 3.7 ounces, which is lighter than the Kodak PIXPRO C1 (5.2 ounces).</p><p>Despite the “Tank” name, the Yashica feels delicate. As you might expect from the cheaper price point of this camera and its light weight, it feels a little flimsy. While it doesn’t feel like it’s going to break on you, it feels more like a toy than a camera. The Pentax WG-1000, for example, feels a lot sturdier and more reliable.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="f8fpL827W3oUkF2h6PRCQF" name="Yashica Tank" alt="Yashica Tank in pink photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/f8fpL827W3oUkF2h6PRCQF.jpg" mos="" align="middle" fullscreen="" width="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 front of the camera sports a faux leather finish made from silicone, which is very comfortable to hold, especially when using it in the selfie position. The flash is located at the top right of the camera, just above the lens. The base of the camera also has a tripod mount next to the battery compartment.</p><p>I have the camera in the light pink color, which can be found on the website labeled under ‘Pink Marshmallow.’ This name should tell you all you need to know about this camera — it’s just for fun. The pink color reminds me of the compact camera I had when I was six years old… and the results are pretty similar too.</p><h3 id="ports-3">Ports</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="V5EaMAkeG5GAfbAtE9fmJF" name="Yashica Tank" alt="Yashica Tank in pink photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/V5EaMAkeG5GAfbAtE9fmJF.jpg" mos="" align="middle" fullscreen="" width="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 Yashica Tank only has a USB-C port for charging and data transfer. It’s great to have this universal port as I was able to plug it in to charge anywhere. The camera uses a microSD card and has no internal storage. </p><h3 id="monitor-2">Monitor</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="HtQD3AZMFaFGW5QpYwDDwE" name="Yashica Tank" alt="Yashica Tank in pink photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/HtQD3AZMFaFGW5QpYwDDwE.jpg" mos="" align="middle" fullscreen="" width="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 want to use the Tank for vlogging or taking selfies, the handy 3-inch LCD screen flips up at a 180º angle. It’s important to note, though, that it has a 360 x 640 resolution and is very pixelated, so you won’t get a good idea of what the image looks like until you download the files. </p><p>The monitor itself looks exactly like the screen of the Kodak PIXPRO C1 and moves in the same way. This LCD screen is very bright, and even on the lowest brightness setting, it was hurting my eyes in low-light settings. </p><h3 id="weather-sealing-3">Weather-sealing</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="udqHGaDraSbkL2Pg6PpsCF" name="Yashica Tank" alt="Yashica Tank in pink photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/udqHGaDraSbkL2Pg6PpsCF.jpg" mos="" align="middle" fullscreen="" width="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 Tank has no official durability rating, so be careful not to drop it or get it wet. For added protection, the camera comes with a small cloth bag that covers the lens and screen, but it won’t do much in adverse conditions. For a weather-sealed option, you should try the Pentax WG-1000, which can withstand drops from a height and is waterproof.</p><h2 class="article-body__section" id="section-yashica-tank-review-controls"><span>Yashica Tank review: Controls</span></h2><p>The Yashica Tank’s controls are very easy to get the hang of. Most of the controls can be found on the back, just to the left of the screen. At the top, you’ll find a dial to zoom in and out, and below this, a thumbrest when using the camera one-handed. You turn the flash on or set it to auto by pushing the joystick upwards. This joystick is also easy to use when navigating through the menu settings. Below this, you’ll find buttons for the display, menu, gallery and bin. </p><p>The menu is easy to use, and you can change color filters here, which is particularly handy when shooting. During setup, I made sure to go into the menu first and make sure all of my settings suited how I use the camera. For me, that meant lowering screen brightness to conserve battery, altering the aspect ratio, and setting stabilization and focus settings. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xi6dTkTNcRXuALofZP2tEF" name="Yashica Tank" alt="Yashica Tank in pink photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/xi6dTkTNcRXuALofZP2tEF.jpg" mos="" align="middle" fullscreen="" width="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 top of the camera, you’ll find the shutter button on the far right, the power button in the middle, and a switch on the left. This switch toggles the camera between video and stills modes. To record with this camera, you have to switch to the video mode and then use the shutter button to start and stop recording. </p><p>You can also only view the videos you take when the switch is on video, and you can only view the pictures when the switch is on photo, which was confusing at first, since I was wondering where all of my photos had disappeared to. </p><h2 class="article-body__section" id="section-yashica-tank-review-lens"><span>Yashica Tank review: Lens</span></h2><p>The Yashica Tank has a fixed focal length of 4.05mm. This is best suited to landscapes, group photos, and street photography, rather than close-up, detailed shots. The aperture of the camera is fixed at f/1.8, meaning a fairly shallow depth of field. The wide aperture is beneficial for low light, though. In my testing, it worked well across most lighting settings, apart from very extreme bright areas — here, it would’ve been useful to have a variable or narrower aperture to reduce overexposure.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AQXML2srd2zrZELvJPfDbF" name="Yashica Tank" alt="Yashica Tank in pink photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/AQXML2srd2zrZELvJPfDbF.jpg" mos="" align="middle" fullscreen="" width="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 don’t get any optical zoom on the camera; instead having to make do with 8x digital zoom. Digital zoom essentially crops into an image to digitally enlarge it. The small 12MP sensor doesn’t give you much resolution in the first place, but you quickly lose a lot of those pixels the more you zoom in, resulting is a low-res image.</p><p>If you want a camera with a better zoom function, I would recommend a compact camera with an optical zoom as it retains the camera’s full sensor resolution at longer focal lengths. I’d suggest the Lumix ZS99, which boasts a whopping 30x optical zoom.</p><h2 class="article-body__section" id="section-yashica-tank-review-image-quality"><span>Yashica Tank review: Image quality </span></h2><p>The Yashica Tank has a 12MP 1/2.8-inch CMOS sensor, which is just a little lower than the Kodak PIXPRO C1 and its 13MP sensor. For a compact with better quality, the <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-wpz2-review">Kodak PIXPRO WPZ2</a> ($179) has a 16MP sensor.  The Tank can use built-in software to upscale (interpolate) internally to 24MP or 36MP,  although this isn’t as good as having more actual pixels to begin with.</p><p>You can’t make any adjustments to the shutter speed of the camera. And on the note of the shutter, there’s a lot of lag between pressing the button and the shutter actually shutting. I often ended up with pictures of the floor due to this. To combat this, I manually turned on the shutter sound in settings and made sure to listen out for it — only moving the camera after I’d heard it.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6dRashz627fqJkNY3Y6oBF" name="Yashica Tank" alt="Yashica Tank in pink photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/6dRashz627fqJkNY3Y6oBF.jpg" mos="" align="middle" fullscreen="" width="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>Any adjustments to the ISO can be made in the camera’s menu. You can select between Auto, ISO 100, 200 and 400. In Auto, the ISO range is 110-6400, which is pretty fine for a camera like this and gives you some flexibility in lower light.</p><p>Image quality on a 12MP compact camera is never going to be the best. The Tank is no exception. Since compact cameras are making a huge comeback amongst younger generations as a bit of fun to capture a vintage, retro aesthetic, you don’t need much more than what you get from the Tank. Generally, the camera does a good job of keeping everything sharp. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/YozrWzWeDn7AoP64n6JnFY.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/WV7LRny4DUPrG6gPirhnvX.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure></figure><p>When using the standard filter setting on the camera, the images felt pretty dull, as you can see in the gallery above, and I don’t think this reflected the color I was seeing in person very well. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/UfSRadtXnhBSi22ZMcc32X.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/BVwtkrBk5bsH3thKg27ndW.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/HsrNe95NHT252DUEAE6kaW.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure></figure><p>As you can see from the pictures above, the camera gives you the option to use color filters that are kind of like film simulations. These include monotone, negative and lomo. Lomo (as seen in the images below) was my favorite option to use as it made all of the colors in the images a lot more saturated, which was perfect for taking pictures of the brightly colored streets of Porto. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/CUtFyFwYp9M5e8Qsw33N9Y.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/UrqRSbYu3Xnpg3chYLqS8Y.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Np3KLDWcgxjThFWJdtBa3Y.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure></figure><p>The camera performs best in settings where the lighting is consistent. However, in scenes with a high dynamic range, where there are dark shadows as well as bright highlights, the camera struggles to capture detail across the full tonal spectrum.</p><p>In the image gallery below, the first image is straight out of the camera. In the shadows, some detail is lost to black, but some of this was recoverable in post-production, as visible in the second image — with the shadows brightened up, extra detail was revealed around the chairs on the left, although shadows were quite noisy. The highlights are also blown out in the brightest spots, on the tops of the umbrellas in the middle. However, for the price of the camera, this is reasonable, and I was expecting a lot worse. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Tj79YqkqZK5mvW6rNWcxtW.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Vq3zfunvqcEXdQyDyeaKJY.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure></figure><p>In the images below, you can see how the Tank’s 8x digital zoom works. A lot of sharpness and clarity was lost as I zoomed into the church and the cloudy park behind, but it’s not the worst thing in the world. It won’t win any awards, but if you want a quick snap of some far-off scenery for yourself, then it’s good enough. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ZPPDv7xQkBpxTDgnMEB6dW.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/PgvWYPobakmLXEfPE2zUyV.jpg" alt="Images taken using the Yashica Tank" /><figcaption><small role="credit">Tom's Guide / Ashley Thieme</small></figcaption></figure></figure><p>The minimum focusing distance is 10cm, so it’s not the best for getting close-up shots, and there’s no macro mode if you want to get really detailed shots. But again, it’s good enough for everyday photography, which is primarily what the Tank is 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:8064px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="FapnLfrssddrrMo6rpyezV" name="Yashica Tank" alt="Images taken using the Yashica Tank" src="https://cdn.mos.cms.futurecdn.net/FapnLfrssddrrMo6rpyezV.jpg" mos="" align="middle" fullscreen="" width="8064" height="4536" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Ashley Thieme)</span></figcaption></figure><p>The autofocus on the Tank uses an AF assist lamp, which you can turn on or off. But at times, this lamp would be captured in the images, leaving me with a red hue over everything. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:8064px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fgYbmchhGvMnonFYFEd2jW" name="Yashica Tank" alt="Images taken using the Yashica Tank" src="https://cdn.mos.cms.futurecdn.net/fgYbmchhGvMnonFYFEd2jW.jpg" mos="" align="middle" fullscreen="" width="8064" height="4536" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide / Ashley Thieme)</span></figcaption></figure><p>I found the autofocus to be good, and thanks to the camera’s flip-up screen, I was able to get my selfie positioning right for the focus to lock in. </p><h2 class="article-body__section" id="section-yashica-tank-review-video"><span>Yashica Tank review: Video</span></h2><p>While I mentioned earlier that you could use the Yashica Tank for video, I wouldn’t recommend it. The camera can shoot in 4K at 30fps which is actually very good for a camera of this size, but the electronic stabilization is shoddy at best, and it picks up on every little movement you make. </p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/hdKgkTlD9ro" allowfullscreen></iframe></div></div><p>As you can see in the above video, it’s very hard to keep the camera steady as I pan around, and when zooming, this becomes even worse. Also, the wind buffeting in the microphone makes it virtually impossible to hear anything.</p><h2 class="article-body__section" id="section-yashica-tank-review-battery-life"><span>Yashica Tank review: Battery life</span></h2><p>The Yashica Tank houses an 800mAh battery, which can capture 220 shots per charge. This would be passable… if it were true. My camera drained a lot faster than that. I had to charge the camera after about 50 shots. Granted, I was using the flash and also had the LCD screen on the highest brightness. When the brightness was turned down, the battery seemed to last a lot longer. You can get a lot more battery out of the Pentax WG-1000, which has enough power for 300 still images. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pgwAB85qwgifsac8XqfHLF" name="Yashica Tank" alt="Yashica Tank in pink photographed in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/pgwAB85qwgifsac8XqfHLF.jpg" mos="" align="middle" fullscreen="" width="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 camera can be charged via the USB-C port. It only took around an hour and a half to fully charge in my testing. There’s an indicator light to let you know when the Tank is charged.</p><h2 class="article-body__section" id="section-yashica-tank-review-verdict"><span>Yashica Tank review: Verdict</span></h2><p>The Yashica Tank is a fairly good choice for anyone looking to hop on the retro camera trend. It’s cheap enough that it’s not going to break the bank, and its compact style means it can easily be thrown in your bag or put in your pocket to take anywhere. The camera has a 180º flip screen, which is perfect for taking selfie-style photos, and the easy-to-use controls mean that anyone can pick up and use the camera. The image quality is fairly respectable, too, giving me good pictures of scenery and selfies, while the filters make stylized photos a piece of cake.</p><p>However, with the low price tag comes cheap build quality. The video to the camera switch on the top of the camera to view each format is annoying. The lack of optical zoom also means that it’s only really suited to wider scenes, as when using the 8x digital zoom, you’ll be sacrificing lots of resolution. You need to get a camera with optical zoom for better zoomed-in quality, like the Kodak PIXPRO WPZ2. I also found the Tank’s delayed shutter annoying at times. </p><p>But with that said, if you need a camera that’s cheap enough that you don’t need to worry about it getting damaged, and you just want to take fun photos on your travels and with friends, you can do worse than the Yashica Tank.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I put my iPhone 17 Pro vs. $1,900 Olympus mirrorless camera through a zoom test — and the results surprised me ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/phones/iphones/i-put-my-iphone-17-pro-vs-olympus-mirrorless-camera-through-a-zoom-test-and-the-results-surprised-me</link>
                                                                            <description>
                            <![CDATA[ I pit the iPhone 17 Pro’s zoom against a $1,898 Olympus mirrorless camera kit to see if a smartphone can truly beat dedicated optical glass at 16x. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">vPmG2YHCZ2jm9t7GZuTSzA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ctCNiPVLvhvXsLEtaZtnzJ-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 06 Jun 2026 11:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[iPhones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                    <category><![CDATA[Phones]]></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/ctCNiPVLvhvXsLEtaZtnzJ-1280-80.jpg">
                                                            <media:credit><![CDATA[John Velasco / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[iPhone 17 Pro vs Olympus E-M10 Mark IV.]]></media:description>                                                            <media:text><![CDATA[iPhone 17 Pro vs Olympus E-M10 Mark IV.]]></media:text>
                                <media:title type="plain"><![CDATA[iPhone 17 Pro vs Olympus E-M10 Mark IV.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ctCNiPVLvhvXsLEtaZtnzJ-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Over the last couple of years, I’ve seen phone makers dial down the optical zoom range of their telephoto cameras. The <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-17-pro-review">iPhone 17 Pro</a> and <a href="https://www.tomsguide.com/phones/iphones/iphone-17-pro-max-review">17 Pro Max</a> are no exception to this radical change, as they ditch the 5x optical zoom of their predecessors for a 4x lens.</p><p>The average person will automatically think it’s a downgrade, but there’s more to it than just hardware zoom. That’s because the iPhone 17 Pro pairs that 4x optical zoom with a much larger 48MP sensor — up from the 12MP sensor on the previous <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-16-pro-review">iPhone 16 Pro</a>. On top of that, the latest iPhones benefit heavily from new advancements in computational photography.</p><p>That’s why I wanted to see how the iPhone 17 Pro’s zoom performs against a modern mirrorless camera to see exactly which one delivers the best results. For this test, I’m specifically going to look at how the iPhone 17 Pro compares at 16x because it better illustrates the enhancements that Apple’s making. Here’s what I found.</p><h2 class="article-body__section" id="section-what-you-need-to-know"><span>What you need to know</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7LEEoNpqPBsk2YnaAeVHuJ" name="iPhone 17 Pro vs Olympus E-M10 Mark IV" alt="iPhone 17 Pro vs Olympus E-M10 Mark IV." src="https://cdn.mos.cms.futurecdn.net/7LEEoNpqPBsk2YnaAeVHuJ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: John Velasco / Tom's Guide)</span></figcaption></figure><p>First, let's talk about the $1,099 cost of the iPhone 17 Pro. That’s up $100 over its predecessor, but it’s one of the <a href="https://www.tomsguide.com/us/best-phone-cameras,review-2272.html">best camera phones</a> around with good reason. Its triple-camera setup consists of a 48MP main sensor, a 48MP ultrawide, and a 48MP telephoto with 4x optical zoom.</p><p>By contrast, the <a href="https://www.tomsguide.com/reviews/olympus-om-d-em-10-mark-iv">Olympus E-M10 Mark IV</a> I’m using packs a 20.3MP Four-Thirds sensor — but the ridiculous telephoto lens I’m pairing it with is what makes the setup just as versatile. I'm using the <a href="https://www.bhphotovideo.com/c/product/1880207-REG/om_system_v335360bw000_m_zuiko_digital_ed_12_200mm.html/" target="_blank" rel="nofollow">Olympus M.Zuiko Digital ED 12-200mm lens</a>, which covers practically every focal length. While the E-M10 Mark IV is largely considered a budget model with a starting price of $799, the lens tacks on a whopping $1,099 to the bottom line. When you add them up, the Olympus kit comes out to $1,898 before any discounts — giving the iPhone 17 Pro a clear initial price advantage.</p><p>To ensure a fair comparison, I’ve adjusted the framing of the Olympus to align with the iPhone 17 Pro at 16x zoom. Because of the Olympus's 2x crop factor, this means setting the lens to a 200mm focal length to perfectly match the iPhone's field of view.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="NN6P7zKNhr8rrq5mSKMEyJ" name="iPhone 17 Pro vs Olympus E-M10 Mark IV" alt="iPhone 17 Pro vs Olympus E-M10 Mark IV." src="https://cdn.mos.cms.futurecdn.net/NN6P7zKNhr8rrq5mSKMEyJ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: John Velasco / Tom's Guide)</span></figcaption></figure><p>Finally, it’s important to mention their resolutions at this distance. The Olympus captures a full-sensor 20.3MP snapshot (5,184 x 3,888 pixels). Meanwhile, the iPhone 17 Pro relies on a 12MP crop on the center of the sensor (4,032 x 3,024 pixels) that uses computational photography to upscale the digital zoom back to size.</p><h2 class="article-body__section" id="section-test-1-building-under-construction"><span>Test 1: Building under construction</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=18b42a7e-5f6a-11f1-ba1b-0e6f42328d7d"></iframe><p>If you’re looking at the comparison shots above on your phone, you’d be hard-pressed to see any major differences between them, apart from the warmer color temperature of the iPhone 17 Pro. There’s also slightly more contrast in the iPhone sample, but it's nothing I’d wager to be a deal-breaker — both are absolutely good enough to share on social media.</p><p>The true advantage for me emerges when I look closer at the details, like the individual bricks on the building. On the surface, it’s pretty clear that both cameras do a great job defining each brick. However, it becomes obvious that the Olympus draws out sharper details and more natural definition once you really inspect the textures.</p><p><strong>Winner:</strong> Olympus E-M10 Mark IV</p><h2 class="article-body__section" id="section-test-2-times-square"><span>Test 2: Times Square</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=511d28f2-5f6a-11f1-ba1b-0e6f42328d7d"></iframe><p>This matchup is fascinating because it showcases the power of the iPhone 17 Pro’s computational photography working heavily in the background. Looking at the two images at face value, it’s an overwhelming win for the iPhone — mainly due to how perfectly it balances the scene's complex exposure. From the rich blue sky to the darker, shadowed sections of the skyscrapers, it dials everything in just right.</p><p>The Olympus really struggles here, overexposing the entire frame in an attempt to compensate for the deep building shadows, which leaves the sky completely blown out. However, I’m still impressed by how well the iPhone 17 Pro matches the dedicated camera in pure definition — which is especially evident when you look closely at the wooden water tower in the lower-middle section of the frame.</p><p><strong>Winner:</strong> iPhone 17 Pro</p><h2 class="article-body__section" id="section-test-3-flower-closeup"><span>Test 3: Flower closeup</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=6bfdc3d4-5f6a-11f1-ba1b-0e6f42328d7d"></iframe><p>At the corner of Broadway and 42nd Street in New York City, there are several large flower beds, so I pointed both devices toward them from across the street. This time, there are distinct qualities I appreciate from both images.</p><p>For starters, the iPhone 17 Pro’s ability to deliver accurate colors and balance the exposure (IMG_1209.jpg) is downright impressive, especially with how well it preserves the finer details and red speckles on the yellow flower’s petals.</p><p>The Olympus (P5270184.jpg) again struggles to rein in the exposure, resulting in the slight highlight clipping you see on the main petals. However, its large sensor and glass lens pull through with a beautifully creamy, natural background blur that gives the entire shot a much more professional, artistic touch. Still, the Olympus blows out too many details.</p><p><strong>Winner:</strong> iPhone 17 Pro</p><h2 class="article-body__section" id="section-test-4-lefcourt-colonial-building"><span>Test 4: Lefcourt Colonial Building</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=882815a0-5f6a-11f1-ba1b-0e6f42328d7d"></iframe><p>There are a lot of ornate details on the Lefcourt Colonial Building thanks to its Neo-Gothic architecture. While these 16x zoom samples look nearly identical at first glance, the matchup highlights the same processing differences I’ve noticed previously. This time, however, the Olympus E-M10 Mark IV nails the exposure on the stone facade a bit better.</p><p>That said, the iPhone 17 Pro’s shot holds up nicely because its deeper contrast delivers a much more dramatic look against the dark windows of the building behind it. You can’t really see the difference unless you crop in close, but the Olympus's true optical zoom proves to be far more effective at resolving the finest textures and sharpest lines in the architecture.</p><p><strong>Winner:</strong> Olympus E-M10 Mark IV</p><h2 class="article-body__section" id="section-test-5-40th-street-philosophers"><span>Test 5: 40th Street Philosophers</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=a3ae857e-60ec-11f1-ba1b-0e6f42328d7d"></iframe><p>Near my office, I often take photos of the 40th Street Philosophers nestled on the side of a building — with the iPhone’s shot getting my attention right away. That’s due to its warmer color temperature and the sharpening effect it applies. Side-by-side, it’s hard to tell which one has the stronger details.</p><p>However, zooming in again reveals the superior advantage of having a longer optical zoom because those fine details around the statue look more defined. That’s something most people will overlook if they just look at the general shot, but there’s a definitive winner the more I zoom into them.</p><p><strong>Winner:</strong> Olympus E-M10 Mark IV</p><h2 class="article-body__section" id="section-test-6-light-blue-pansy"><span>Test 6: Light Blue Pansy</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=9ff2e44e-5f6a-11f1-ba1b-0e6f42328d7d"></iframe><p>The great thing about shooting with a long telephoto zoom is that it can sometimes mimic the tight framing of a macro shot. The images of the light blue pansy above are a perfect example of this, as both cameras do a nice job of isolating the flower from the background pavement. I’ll admit that the iPhone 17 Pro’s sample is better exposed, but to my eyes, it captures just as much raw detail and definition as its competitor.</p><p>I say this because zooming heavily into the pansy itself doesn't reveal any meaningful sharpness advantages for the Olympus. Plus, the dedicated camera's underexposure causes the surrounding green leaves to lose a noticeable amount of definition in the shadows.</p><p><strong>Winner:</strong> iPhone 17 Pro</p><h2 class="article-body__section" id="section-test-7-a-much-closer-shot-of-a-building"><span>Test 7: A much closer shot of a building</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=b4b69308-5f6a-11f1-ba1b-0e6f42328d7d"></iframe><p>So far, my previous skyscraper shots were taken from long distances, making them heavily affected by atmospheric distortion. For this next test, I captured the building from ground level looking straight up — eliminating all that extra air. This time, the results are pretty surprising because the iPhone 17 Pro does everything right, matching the Olymous' level of detail in the brickwork.</p><p>Meanwhile, the Olympus sample looks flat and again suffers from the same overexposure issues noticed in my earlier tests. The camera blows out the bright highlights, causing a significant amount of brick detail on the sunlit facade to be completely lost.</p><p><strong>Winner:</strong> iPhone 17 Pro</p><h2 class="article-body__section" id="section-test-8-perfume-on-a-shelf"><span>Test 8: Perfume on a shelf</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=cf9aa376-5f6a-11f1-ba1b-0e6f42328d7d"></iframe><p>Moving indoors for this next test, I can easily make out the label on the perfume bottle in both shots, but a few key details give the Olympus the upper hand here. Not only is the Olympus significantly less noisy than the iPhone's, but the tiny fine print at the bottom of the bottle is much more legible. The dedicated camera also pulls off a slightly shallower depth of field, blurring the background bottles more effectively.</p><p>The iPhone 17 Pro’s snapshot is still adequate for casual use, but at this 16x digital stretch, it lacks the raw resolving power of the Olympus to sharpen that microscopic text without introducing heavy processing artifacts.</p><p><strong>Winner:</strong> Olympus E-M10 Mark IV</p><h2 class="article-body__section" id="section-test-9-co-worker-portrait"><span>Test 9: Co-worker portrait</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=e73026d2-5f6a-11f1-ba1b-0e6f42328d7d"></iframe><p>While at the Tom’s Guide office, I captured a few portraits of my colleague Arif Bacchus — and they perfectly reinforce what I already know about these two setups. Immediately, I can see how the sharpness of the Olympus captures the finer, more natural details across his face, while its optical glass smoothly blurs out the background elements.</p><p>The iPhone 17 Pro’s shot is still decent, but it's overly warm and the finer details and overall definition are noticeably softer by comparison. Most people probably wouldn't spot the degradation if they only view the photo on a small phone screen, but the difference becomes glaringly obvious when you look at the samples on a larger monitor.</p><p><strong>Winner: </strong>Olympus E-M10 Mark IV</p><h2 class="article-body__section" id="section-test-10-moon-shot"><span>Test 10: Moon shot</span></h2><iframe allow="" height="452" width="100%" id="" style="" class="position-center" data-lazy-priority="low" data-lazy-src="https://cdn.knightlab.com/libs/juxtapose/latest/embed/index.html?uid=fdcd77dc-5f6a-11f1-ba1b-0e6f42328d7d"></iframe><p>And finally, there’s the ultimate zoom test: a shot of the moon! This is a classic challenge for smartphone telephotos due to the reputation for how some phones digitally alter or enhance the scene. Because the moon is a glaringly bright speck against a pitch-black night sky, I had to rely heavily on manual touch-focusing and exposure sliders to pull out any usable detail. I ultimately had to drop the phone's exposure value all the way down, but the iPhone 17 Pro still managed a passable shot.</p><p>Meanwhile, I had to take similar steps with the Olympus because leaving it on automatic mode completely overexposed the moon into a featureless white blob. After switching over to full manual controls, I was able to dial back the exposure to cleanly capture the distinct craters and darker plains across the lunar surface. The mirrorless camera's sample is vastly more detailed, better exposed, and far more true-to-life.</p><p><strong>Winner: </strong>Olympus E-M10 Mark IV</p><h2 class="article-body__section" id="section-bottom-line"><span>Bottom line</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pWdoJPkyuW7tYupie2JUwJ" name="iPhone 17 Pro vs Olympus E-M10 Mark IV" alt="iPhone 17 Pro vs Olympus E-M10 Mark IV." src="https://cdn.mos.cms.futurecdn.net/pWdoJPkyuW7tYupie2JUwJ.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: John Velasco / Tom's Guide)</span></figcaption></figure><p>Am I surprised that the Olympus E-M10 Mark IV won more rounds than the iPhone 17 Pro? Not really. But then again, that dedicated camera setup demands a hefty $1,900 investment — which isn’t cheap by any means. The iPhone is still hundreds of dollars cheaper and proves how Apple’s years of refinement in computational photography allow it to consistently deliver better-exposed scenes.</p><p>When I look at all these comparison photos at face value, it’s incredibly impressive how far smartphone zoom technology has come. The phone's internal hardware is doing a fraction of the work, while software and image processing algorithims handle the heavy lifting.</p><p>While it ultimately lacks the sheer resolving power of the Olympus and its dedicated optical glass, the iPhone 17 Pro’s telephoto zoom performance is still absolutely worthy of sharing on social platforms.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-W3woDW"></div>                            </div>                            <script src="https://kwizly.com/embed/W3woDW.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/phones/android-phones/this-camera-feature-is-so-good-the-iphone-18-pro-absolutely-need-to-copy-it">This camera feature is so good Apple needs to copy it for the iPhone 18 Pro</a></li><li><a href="https://www.tomsguide.com/phones/iphones/iphone-18-pro-leaked-colors-include-a-stunning-new-dark-cherry-option">iPhone 18 Pro leaked colors include a stunning new 'Dark Cherry' option</a></li><li><a href="https://www.tomsguide.com/entertainment/streaming-devices/is-a-new-apple-tv-in-the-works-heres-how-john-ternus-tenure-as-apple-ceo-could-benefit-apples-streaming-device">Is a new Apple TV in the works? Here's how John Ternus' appointment as Apple CEO could benefit Apple's streaming device</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ DJI Osmo Pocket 4: two US-only AliExpress codes have landed, and they’re gone after June 30 ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-pocket-4-aliexpress-codes</link>
                                                                            <description>
                            <![CDATA[ DJI Osmo Pocket 4: two US-only AliExpress codes have landed, and they’re gone after June 30 ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ZrYwPuLmWicdiZhR2Axwsi</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/QD9u6T39KMHHMQDnk4jixQ-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 02 Jun 2026 16:46:19 +0000</pubDate>                                                                                                                                <updated>Tue, 16 Jun 2026 10:49:52 +0000</updated>
                                                                                                                                            <category><![CDATA[GoPros &amp; Action Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                                    <dc:creator><![CDATA[ Nathan Walters ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/SrGsFnCdBA6mk62P5mBVuN.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nathan is an SEO copywriter for the vouchers team, so he knows how to save on the latest tech and games. Starting as a Deals Finder for MyVoucherCodes, he used his money-saving skills to&lt;a href=&quot;https://www.myvouchercodes.co.uk/resources/technology&quot;&gt; &lt;u&gt;write blogs and money-saving content about tech retailers&lt;/u&gt;&lt;/a&gt;. He has since covered topics such as graphics cards, mobile phones, game consoles and TVs for sites like PC Gamer, TechRadar, and Tom’s Hardware. He has also written money-saving advice for multiple publications such as Moneynet, NationalWorld, iPaper, Pick Me Up! And My Weekly. In addition to saving people money on their new tech, Nathan loves gaming and building PCs and is always looking for a good deal on graphics cards or video game keys.&lt;/p&gt;&lt;p&gt;Nathan has worked at Future since 2022 and has written about various big sale events such as Black Friday, Amazon Prime Day, Cyber Monday, and Christmas. His Deal Finder experience, combined with his voucher writing experience, has given him the skills needed to find amazing deals and update you on the latest and best ways to save on your next purchase. Nathan’s work has helped readers find the best-value gaming monitors, games, TVs, and streaming sites.&lt;/p&gt;&lt;p&gt;&lt;br&gt;Born in the South Wales Valleys, Nathan is an avid video game and tabletop player who loves writing. When not finding tech deals, he can be found playing the latest games, playing D&amp;D, or writing game reviews for his website,&lt;a href=&quot;https://www.gamereport.co.uk/&quot;&gt; &lt;u&gt;GameReport&lt;/u&gt;&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/QD9u6T39KMHHMQDnk4jixQ-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[DJI Osmo Pocket 4 against a blue background]]></media:description>                                                            <media:text><![CDATA[DJI Osmo Pocket 4 against a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[DJI Osmo Pocket 4 against a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/QD9u6T39KMHHMQDnk4jixQ-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>This is the sort of deal worth pouncing on while it’s live!</p><p>DJI’s Osmo Pocket 4 only turned up in April, and it’s already the little camera every solo creator seems to be raving about. Now AliExpress US has two amazing coupon codes that knock a big chunk off both the standalone camera and the full Creator Combo, and it has free shipping from US warehouses, with buyer protection. No messing about with cross-border shipping or duties. </p><p>We at Tom's Guide already named it the "best all-in-one package” for solo creators, and <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review" target="_blank">one of the best action cameras of 2026</a>, so trust us when we say this is a great deal.</p><p>But here’s the bit you came for: use code <strong>USAFF93</strong> on the <a href="https://aliexpress.sjv.io/c/10132/3916414/22364" target="_blank" rel="nofollow">standard Osmo Pocket 4,</a> and you’ll only pay $575.71. Fancy the full vlogging kit instead? Code <strong>USAFF120</strong> <a href="https://aliexpress.sjv.io/c/10132/3916418/22364" target="_blank" rel="nofollow">brings the Creator Combo</a> (the DJI Mic 3, wide-angle lens, battery handle and tripod) down to $699.05. Both are US-only and are live right through to the end of June. </p><p>These codes are not available on DJI.com, Amazon US, or B&H, so make sure you grab them from here while the deal lasts!</p><h2 id="quick-links">Quick Links</h2><div class="product"><a data-dimension112="c00782b4-4219-4928-a7bc-f8957df6471e" data-action="Deal Block" data-label="Check it out on AliExpress" data-dimension48="Check it out on AliExpress" href="https://aliexpress.sjv.io/c/10132/3916414/22364" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2126px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="3AribytNBs5zZreZAqdDmW" name="Aliexpress logo" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/3AribytNBs5zZreZAqdDmW.png" mos="" align="middle" fullscreen="" width="2126" height="2126" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>DJI Osmo Pocket 4 (Standard) for $509.98 using the code <strong>MYFUTURE90</strong>→ <a href="https://aliexpress.sjv.io/c/10132/3916414/22364" target="_blank" rel="nofollow" data-dimension112="c00782b4-4219-4928-a7bc-f8957df6471e" data-action="Deal Block" data-label="Check it out on AliExpress" data-dimension48="Check it out on AliExpress" data-dimension25=""><u>Check it out on AliExpress</u></a></p><p>DJI Osmo Pocket 4 Creator Combo for $635.24 using the code <strong>MYFUTURE120 </strong>→ <a href="https://aliexpress.sjv.io/c/10132/3916418/22364" target="_blank" rel="nofollow"><u>Check it out on AliExpress</u></a></p><p><em>Codes are case-sensitive and single-use per account, US-only, valid June 1 to 30. Ships free from US warehouses in 2 to 5 business days, with the manufacturer's warranty as listed. </em><a class="view-deal button" href="https://aliexpress.sjv.io/c/10132/3916414/22364" target="_blank" rel="nofollow" data-dimension112="c00782b4-4219-4928-a7bc-f8957df6471e" data-action="Deal Block" data-label="Check it out on AliExpress" data-dimension48="Check it out on AliExpress" data-dimension25="">View Deal</a></p></div><h2 id="save-on-the-dji-osmo-pocket-4">Save on the DJI Osmo Pocket 4</h2><div class="product"><a data-dimension112="7a7c6871-6291-46f7-b655-cdb78d132d78" data-action="Deal Block" data-label="DJI Osmo Pocket 4 (Standard) for only $575.71 with code" data-dimension48="DJI Osmo Pocket 4 (Standard) for only $575.71 with code" data-dimension25="$509.98" href="https://aliexpress.sjv.io/c/10132/3916414/22364" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:640px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="8mnn9bPajLA7jxxsnzoU2S" name="dji osmo pocket 4 standalone" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8mnn9bPajLA7jxxsnzoU2S.png" mos="" align="middle" fullscreen="" width="640" height="640" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p><a href="https://aliexpress.sjv.io/c/10132/3916414/22364" target="_blank" rel="nofollow" data-dimension112="7a7c6871-6291-46f7-b655-cdb78d132d78" data-action="Deal Block" data-label="DJI Osmo Pocket 4 (Standard) for only $575.71 with code" data-dimension48="DJI Osmo Pocket 4 (Standard) for only $575.71 with code" data-dimension25="$509.98">DJI Osmo Pocket 4 (Standard) for only $575.71 with code</a> <strong>MYFUTURE90</strong></p><p><a href="https://www.tomsguide.com/reviews/dji-osmo-pocket-3" target="_blank" rel="nofollow">The Osmo Pocket 3 was already brilliant</a>, and the <a href="https://aliexpress.sjv.io/c/10132/3916414/22364" target="_blank" rel="nofollow">Pocket 4</a> is a proper glow-up rather than a small token refresh. DJI kept the 1-inch CMOS sensor and that lovely f/2.0 lens, then doubled the slow-mo to 4K/240fps, added 14 stops of dynamic range with 10-bit D-Log (DJI’s flat color profile that leaves you room to grade), and bumped stills to 37MP. <a class="view-deal button" href="https://aliexpress.sjv.io/c/10132/3916414/22364" target="_blank" rel="nofollow" data-dimension112="7a7c6871-6291-46f7-b655-cdb78d132d78" data-action="Deal Block" data-label="DJI Osmo Pocket 4 (Standard) for only $575.71 with code" data-dimension48="DJI Osmo Pocket 4 (Standard) for only $575.71 with code" data-dimension25="$509.98">View Deal</a></p></div><p>That 4K/240fps is the headline, and rightly so. It turns ordinary b-roll into buttery, ten-times slow motion without dropping the resolution to get there. Which brings me to the upgrade I’d genuinely shout about: storage. You get 107GB built in, running at up to 800MB/s, so no more digging through your bag for a microSD at the exact second the shot appears. Of course, 107GB is your max, since there’s no card slot, so multi-day shooters will want to plan around it. But that's a small price to pay, I reckon. </p><p>Wrap in ActiveTrack 7.0 that keeps you locked in frame even when you zoom, the rotatable screen, that new 5D joystick, and roughly 35% less weight than the Pocket 3, a huge difference, making it a doddle to run one-handed all day. </p><p>If you only want the camera, the standard model at only $509.98 with the AliExpress code does the job nicely. </p><h2 id="save-on-the-creator-combo">Save on the Creator Combo</h2><div class="product"><a data-dimension112="6bf282d5-1cd8-4d3b-af2e-632bf2fdc1db" data-action="Deal Block" data-label="The Creator Combo is the one I’d pick up" data-dimension48="The Creator Combo is the one I’d pick up" data-dimension25="$635.24" href="https://aliexpress.sjv.io/c/10132/3916418/22364" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="PaJRf2SrFqjHGXbJQHD9P" name="DJI osmo 4 creator bundle" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/PaJRf2SrFqjHGXbJQHD9P.webp" mos="" align="middle" fullscreen="" width="2000" height="2000" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>But for vlogging, YouTubing, Journalism, travel or sit-down interviews? <a href="https://aliexpress.sjv.io/c/10132/3916418/22364" target="_blank" rel="nofollow" data-dimension112="6bf282d5-1cd8-4d3b-af2e-632bf2fdc1db" data-action="Deal Block" data-label="The Creator Combo is the one I’d pick up" data-dimension48="The Creator Combo is the one I’d pick up" data-dimension25="$635.24">The Creator Combo is the one I’d pick up</a>, no contest and for $635.24 with code <strong>MYFUTURE120 </strong>its a great deal.</p><p>For only around $125 more than the camera on its own, you bag the DJI Mic 3 (with wireless audio and a built-in mic that levels up your whole set-up), plus the wide-angle lens, battery handle and tripod. <a class="view-deal button" href="https://aliexpress.sjv.io/c/10132/3916418/22364" target="_blank" rel="nofollow" data-dimension112="6bf282d5-1cd8-4d3b-af2e-632bf2fdc1db" data-action="Deal Block" data-label="The Creator Combo is the one I’d pick up" data-dimension48="The Creator Combo is the one I’d pick up" data-dimension25="$635.24">View Deal</a></p></div><p>At $635.24 all in, that’s a proper grab-and-go vlogging kit for under $700, and buying all those parts and putting them together separately would set you back way more. </p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review" target="_blank">I tested DJI’s new Osmo Pocket 4 — this is the only tool I'll recommend to content creators from now on</a></li><li><a href="https://www.tomsguide.com/audio/dji-mic-3-review-the-gold-standard-for-content-creation" target="_blank">DJI Mic 3 review: The gold standard for content creation</a></li><li><a href="https://www.tomsguide.com/audio/dji-mic-mini-2-review" target="_blank">I’ve been testing the brand-new DJI Mic Mini 2 for three weeks — and it’s the best budget mic for content creators</a></li></ul><p><em>Tom’s Guide may earn a commission when you buy through the links in this article.</em></p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ One of the coolest gadgets of 2026 beats my phone at astrophotography — and captures unbelievable nighttime timelapses ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/phones/one-of-the-coolest-gadgets-of-2026-beats-my-phone-at-astrophotography-and-captures-unbelievable-nighttime-timelapses</link>
                                                                            <description>
                            <![CDATA[ The Seestar S30 Pro is a compact smart telescope that combining crisp 4K deep-sky imaging with a wide-angle lens that captures impressive timelapses. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">77nHR3F4S5WVBDEHAryHwP</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/5WTPJvug5qfa2Fa4s4U3yZ-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 01 Jun 2026 08:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Phones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></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/5WTPJvug5qfa2Fa4s4U3yZ-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide / John Velasco]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Seestar S30 Pro review.]]></media:description>                                                            <media:text><![CDATA[Seestar S30 Pro review.]]></media:text>
                                <media:title type="plain"><![CDATA[Seestar S30 Pro review.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/5WTPJvug5qfa2Fa4s4U3yZ-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Nothing relaxes me more than a nighttime astrophotography session in my backyard. Since the start of the year, I’ve been testing a new smart telescope: the <a href="https://www.seestar.com/products/seestar-s30-pro" target="_blank" rel="nofollow">Seestar S30 Pro</a>. While it looks almost identical to the <a href="https://www.tomsguide.com/phones/im-pairing-my-smartphone-with-this-smart-telescope-for-the-next-solar-eclipse-this-weekend-heres-why">Seestar S30</a> I used last year, the nebulas, galaxies, and star clusters I’ve captured look far more polished thanks to the upgraded hardware.</p><p>It all stems from the Sony IMX585 (STARVIS 2) sensor in its telephoto lens. Unlike the standard S30, it delivers full 4K resolution and vastly superior dynamic range. At the same time, the larger sensor yields a much wider field of view, allowing it to capture larger deep-space objects — like the Horsehead and Flame nebulas — in their entirety.</p><p>But the coolest part isn’t just that it shoots better astrophotography than my phone, it also captures some of the most epic nighttime timelapse clips I’ve ever recorded. Honestly, it’s a machine built to capture time in the most amazing way possible.</p><div class="product"><a data-dimension112="1c1efc9a-bb9c-46c3-b6d1-29ca6add0b00" data-action="Deal Block" data-label="Price check: $699 @ Seestar" data-dimension48="Price check: $699 @ Seestar" data-dimension25="$699" href="https://www.amazon.com/ZWO-Astrophotography-App-Controlled-Processing-Wide-Field/dp/B0GYX9GSVX/" 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="NJJqBfnHTkGuUbVtTEvmQ9" name="Seestar S30 Pro deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/NJJqBfnHTkGuUbVtTEvmQ9.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This compact sized smart telescope is travel friendly, allowing you to take it to places to capture astrophotography at night. What makes the Seestar S30 Pro even better is that it can also shoot stunning timelapse clips of the night sky. Everything is controlled through your phone and app, so all you have to do is point it to a target and it'll do the rest.<br><strong>Price check: </strong><a href="https://www.seestar.com/products/seestar-s30-pro" target="_blank" rel="nofollow" data-dimension112="1c1efc9a-bb9c-46c3-b6d1-29ca6add0b00" data-action="Deal Block" data-label="Price check: $699 @ Seestar" data-dimension48="Price check: $699 @ Seestar" data-dimension25="$699"><strong>$699 @ Seestar</strong></a><a class="view-deal button" href="https://www.amazon.com/ZWO-Astrophotography-App-Controlled-Processing-Wide-Field/dp/B0GYX9GSVX/" target="_blank" rel="nofollow" data-dimension112="1c1efc9a-bb9c-46c3-b6d1-29ca6add0b00" data-action="Deal Block" data-label="Price check: $699 @ Seestar" data-dimension48="Price check: $699 @ Seestar" data-dimension25="$699">View Deal</a></p></div><h2 id="movement-of-the-moon-and-sun">Movement of the moon and sun</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="YDDzGxGq4sJHxpywJnv8jZ" name="Seestar-S30-Pro-1-HERO" alt="Seestar S30 Pro lens closeup." src="https://cdn.mos.cms.futurecdn.net/YDDzGxGq4sJHxpywJnv8jZ.jpg" mos="" align="middle" fullscreen="" width="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 / John Velasco)</span></figcaption></figure><p>You would think that by now I'd be tired of imaging the same things in the night sky. Because really, how many times do I need another shot of the moon? Most of the <a href="https://www.tomsguide.com/us/best-phone-cameras,review-2272.html">best camera phones</a> on the market can pull off impressive lunar shots, capturing small details like craters and darker maria. But no matter how much phone manufacturers try to convince me otherwise, the Seestar S30 Pro proves why smart telescopes are lightyears ahead in terms of quality.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/bQtLuMepRVNh4a6JLy3ff4.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Nu84bQLyqWCtm5Mjxn5of4.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/oHhzTTsxKzh9oEnejLqug4.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/S2zkhVy4xdVCVPcX6Nfpi4.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/sWhML4cpLxDxCrhtZyhPe4.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mFArEJWRugHZ5RWbja5Hf4.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>Just look at the shots I captured straight out of the telescope, without any additional enhancements or editing. The details are rich and plentiful, and even when I apply a digital zoom through the app, it still produces a true-to-life image that phones simply can’t match. But the coolest part is using the Seestar S30 Pro to record a timelapse.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/fXyYseRyRKrGwN7aFdjkwk.gif" alt="Seestar S30 Pro timelapse." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MHdvZdmy4z8jCmbRbpAydn.gif" alt="Seestar S30 Pro timelapse." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>A timelapse reveals how the moon subtly changes position and phase over a couple of hours — movements you can only appreciate when time is sped up. It’s a similar story with the sun once I install the included solar filter. Not only does the S30 Pro expose crisp sunspots on its surface, but a timelapse reveals how they shift across the solar disk due to the sun's rotation.</p><h2 id="unmatched-performance">Unmatched performance</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="up5oibJLFCLbdqwhLjt3jZ" name="Seestar-S30-Pro-1-HERO" alt="Seestar S30 Pro shooting astrophotography." src="https://cdn.mos.cms.futurecdn.net/up5oibJLFCLbdqwhLjt3jZ.jpg" mos="" align="middle" fullscreen="" width="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 / John Velasco)</span></figcaption></figure><p>Don’t get me wrong, smartphones have come a long way, and so has their astrophotography performance. Over the last six months, I’ve used the <a href="https://www.tomsguide.com/phones/samsung-phones/i-shot-these-low-light-photos-with-the-galaxy-s26-ultras-hidden-expert-raw-mode-and-the-difference-is-night-and-day">Galaxy S26 Ultra</a>, <a href="https://www.tomsguide.com/phones/google-pixel-phones/i-tried-astrophotography-with-my-google-pixel-10-pro-xl-and-i-caught-something-truly-special">Pixel 10 Pro XL</a>, and <a href="https://www.tomsguide.com/phones/iphones/i-tried-astrophotography-on-the-iphone-17-pro-to-shoot-galaxies-nebulas-and-star-clusters-these-were-my-best-captures">iPhone 17 Pro </a>to shoot the night sky. While they do a decent job of capturing those twinkling stars, they simply lack the telephoto reach to resolve distant galaxies and nebulas.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/s5sVv8oHCzHQsVBLDcXJ95.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/bs9hwkrHHEe3sT8YEzbri5.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/CWYQnxogfgNvSdEXwNUqj5.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/t8NzEg2ECH8Stxgxcey4j5.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/FhvoQ4qUEy66RNfsEAtnk5.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/cs8GHygw4LYpCzMF46Mgk5.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/qmjS57TFAHZp3nBSVZ2YZ5.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9Q3AyYmxRCrTtinM9ucSR5.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Gr9SGmwXBHJ2JhGyLsEk5.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VWPndhBh39pE8t9JX7v9A5.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/kPKR5XvzHzm5MQX5qxbor4.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/oR9bV2JNfhWb4WV8sYb955.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fALAqqmemdCjumDcjL3cz4.jpg" alt="Seestar S30 Pro sample." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>The Seestar S30 Pro can turn any beginner into an expert after just a few sessions because you just select a target through the companion app, and the telescope takes care of the rest. There is simply no comparison to the images it produces. The beauty of the S30 Pro's wider field of view is its ability to easily handle massive deep-sky targets. For instance, I love that I can capture the Horsehead and Flame nebulas together in a single, perfectly framed shot.</p><p>Other smart telescopes, like the <a href="https://www.tomsguide.com/phones/im-pairing-my-smartphone-with-this-smart-telescope-for-the-next-solar-eclipse-this-weekend-heres-why">Unistellar Odyssey Pro</a> and <a href="https://www.tomsguide.com/phones/this-smart-telescope-is-light-years-better-than-phones-at-astrophotography-and-it-lets-me-capture-stars-galaxies-and-nebulas-with-ease">Seestar S50</a>, feature longer focal lengths that allow them to zoom in much closer — but they struggle with expansive targets like the Andromeda Galaxy. Thanks to the S30 Pro's broader view, some of my favorite captures over the past three months include the Rosette Nebula (NGC 2244), the Needle Galaxy (NGC 4565), the Pinwheel Galaxy (M101), and the stunning Whirlpool Galaxy (M51).</p><h2 id="astrophotography-timelapse">Astrophotography timelapse</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vUCmLsBHaCJrebESRcVZpZ" name="Seestar-S30-Pro-1-HERO" alt="Seestar S30 Pro shooting astrophotography." src="https://cdn.mos.cms.futurecdn.net/vUCmLsBHaCJrebESRcVZpZ.jpg" mos="" align="middle" fullscreen="" width="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 / John Velasco)</span></figcaption></figure><p>All of this brings me to why I’ve fallen in love with the Seestar S30 Pro: its remarkable timelapse ability. Because it stays completely stationary on its tripod, I can switch over to the secondary 4K wide-angle camera. Armed with a Sony IMX586 sensor and a wide 63-degree field of view, it keeps the horizon locked while capturing the mesmerizing sweep of the night sky.</p><p>The results are breathtaking. Most of the clips above are 15 to 30 seconds long, but I’ve sped them up and converted them into gif animations. It’s genuinely fascinating to see what happens to the sky over a few hours — and I’m not just talking about the stars. In fact, some of my favorite timelapses show fast-moving clouds rolling across the frame, dramatically parting to reveal the vibrant constellations hidden behind them.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/zyceubam3diRSHJrHQTxon.gif" alt="Seestar S30 Pro timelapse." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/kw47QzDzk44T9qBGk9L5Ao.gif" alt="Seestar S30 Pro timelapse." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9CtFiTfQotbBYS2wEAHUPn.gif" alt="Seestar S30 Pro timelapse." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Uc4z53yU89CEswdQegK5wm.gif" alt="Seestar S30 Pro timelapse." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Lh8QRGErhbkevEPGFQLYGm.gif" alt="Seestar S30 Pro timelapse." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LcxU4GuuknZwCNJdkrAZvm.gif" alt="Seestar S30 Pro timelapse." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mcoykCAWyoLCVjCzkRiyZn.gif" alt="Seestar S30 Pro timelapse." /><figcaption><small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>Even though competitors like the <a href="https://www.tomsguide.com/phones/forget-galaxy-s26-ultra-space-zoom-this-smart-telescope-fits-in-my-pocket-and-captures-galaxies-way-better">Dwarf Mini</a> are slightly more compact, the Seestar S30 Pro’s sheer performance and versatility remain unmatched. While I might have a tough time deciding which smart telescope to pack for a quick road trip, the Seestar is easily the better-equipped machine for tracking deep-sky objects and producing jaw-dropping nightscape timelapses.</p><p>Ultimately, the Seestar S30 Pro lives up to its promise as one of the coolest gadgets I've tested so far in 2026. It bridges the gap between casual smartphone photography and complex, traditional astrophotography rigs. By packing a crisp 4K telephoto sensor, advanced tracking, and a stellar wide-angle timelapse mode into a single portable package, it doesn't just look at the universe — it captures time itself.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Wnmmbe"></div>                            </div>                            <script src="https://kwizly.com/embed/Wnmmbe.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/phones/buying-a-new-phone-heres-how-to-avoid-paying-for-pricey-storage-upgrades">Buying a new phone? Here's how to avoid paying for pricey storage upgrades</a></li><li><a href="https://www.tomsguide.com/phones/i-discovered-a-hidden-android-feature-that-shows-which-apps-are-watching-me">I discovered a hidden Android feature that shows which apps are watching me, and what it revealed was startling</a></li><li><a href="https://www.tomsguide.com/computing/hardware/apple-raises-trade-in-value-for-products-heres-how-much-more-youll-get-for-an-old-iphone-ipad-macbook-or-apple-watch">Apple raises trade-in value for products — here's how much more you'll get for an old iPhone, iPad, MacBook or Apple Watch</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I put an action cam on my cat and let him roam wild — here's what he saw ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/gopro-action-cameras/i-put-an-action-cam-on-my-cat-and-let-him-roam-wild-heres-what-he-saw</link>
                                                                            <description>
                            <![CDATA[ I put the Insta360 Go 3S on my cat — here's the video it captured ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">N4Y2598vMvNTQtucB4DVzV</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/MDhng6J6hLxN6eAqaFk4hT-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 30 May 2026 07:45:00 +0000</pubDate>                                                                                                                                <updated>Mon, 01 Jun 2026 01:49:14 +0000</updated>
                                                                                                                                            <category><![CDATA[GoPros &amp; Action Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ mike.prospero@futurenet.com (Mike Prospero) ]]></author>                    <dc:creator><![CDATA[ Mike Prospero ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/6ZM8mX4UwccqDJTh9gLPqV.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Michael A. Prospero is the U.S. Editor-in-Chief for Tom’s Guide. He oversees all evergreen content and oversees the Homes, Smart Home, and Fitness/Wearables categories for the site. In his spare time, he also tests out the latest drones, electric scooters, and smart home gadgets, such as video doorbells. Before his tenure at Tom&#039;s Guide, he was the Reviews Editor for Laptop Magazine, a reporter at Fast Company, the Times of Trenton, and, many eons back, an intern at George magazine. He received his undergraduate degree from Boston College, where he worked on the campus newspaper The Heights, and then attended the Columbia University school of Journalism. When he’s not testing out the latest running watch, electric scooter, or skiing or training for a marathon, he’s probably using the latest sous vide machine, smoker, or pizza oven, to the delight — or chagrin — of his family.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/MDhng6J6hLxN6eAqaFk4hT-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[insta360 go3s on cat]]></media:description>                                                            <media:text><![CDATA[insta360 go3s on cat]]></media:text>
                                <media:title type="plain"><![CDATA[insta360 go3s on cat]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/MDhng6J6hLxN6eAqaFk4hT-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>A few years ago, <a href="https://www.tomsguide.com/features/i-strapped-an-action-camera-on-my-cat-check-out-what-he-saw">I put an action camera on my cat</a> — the Insta360 GO 3 — to see what he saw as he roamed around our yard. Since then, Insta360 has come out with a newer version of its thumb-sized camera, so I wanted to see how it would perform.</p><p>The <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-go-3s-review">Insta360 GO 3S</a> has an upgraded 4K camera, as well as a wide-angle lens so you can capture more of your surroundings. But what I really like about the camera — especially if you're going to send it out in the wild — is that it has Apple FindMy built in, so that if it somehow fell off my cat's collar, I'd be able to find it more easily.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/HsIGj6e_eEw" allowfullscreen></iframe></div></div><p>When I first did this experiment, I put the Insta360 on what was then our younger cat, who was then about six or seven. Since then, we've adopted a younger cat, who's about a year and a half old, so he's a bit more inclined to run around.</p><p>Well guess what? Regardless of age, cats spend a lot of their time just sitting and looking at stuff, so much of the footage I captured was less than what you would call cinematic.</p><div class="product"><a data-dimension112="12118534-a682-4924-9fff-529c84c41fd2" data-action="Deal Block" data-label="The Insta360 GO 3S can capture 4K video at 30 fps. The camera can last up to 38 minutes, and up to 140 minutes when attached to the Action Pod. You can get it with either 64GB or 128GB of internal storage" data-dimension48="The Insta360 GO 3S can capture 4K video at 30 fps. The camera can last up to 38 minutes, and up to 140 minutes when attached to the Action Pod. You can get it with either 64GB or 128GB of internal storage" data-dimension25="$294" href="https://store.insta360.com/product/go-3s" 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="TLsFVoVyyJ4orkwY3cYceH" name="insta360-go-3s--128-gb-black-4k-tiny-por-7b8476e7-c419-4649-9706-fd95eea98f22.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/TLsFVoVyyJ4orkwY3cYceH.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Insta360 GO 3S can capture 4K video at 30 fps. The camera can last up to 38 minutes, and up to 140 minutes when attached to the Action Pod. You can get it with either 64GB or 128GB of internal storage<a class="view-deal button" href="https://store.insta360.com/product/go-3s" target="_blank" rel="nofollow" data-dimension112="12118534-a682-4924-9fff-529c84c41fd2" data-action="Deal Block" data-label="The Insta360 GO 3S can capture 4K video at 30 fps. The camera can last up to 38 minutes, and up to 140 minutes when attached to the Action Pod. You can get it with either 64GB or 128GB of internal storage" data-dimension48="The Insta360 GO 3S can capture 4K video at 30 fps. The camera can last up to 38 minutes, and up to 140 minutes when attached to the Action Pod. You can get it with either 64GB or 128GB of internal storage" data-dimension25="$294">View Deal</a></p></div><p>Another thing to bear in mind is that, although the Insta360 Go 3S can take 4K video, its battery is still pretty small. If you set the camera to continuously record, you'll end up with less than half an hour of footage.</p><p>When I set the camera to 2.7k/30 fps, I ended up with roughly 34 minutes of video; I then set the resolution to 1080p/30 fps, and was able to eke out nearly 37 minutes. The latter figure is close to the advertised runtime of 38 minutes; the older Go 3 can last a little longer, up to 45 minutes.</p><p>Probably the best video I (or he) took was when he was playing around with a small aluminum foil ball. He discovered early on that he could pick up the ball with his mouth and carry it to a spot where he could more easily bat it with his paws. </p>                    <div class= "tiktok-wrapper" style="min-height: 750px;"><blockquote class="tiktok-embed" cite="https://www.tiktok.com/@tomsguide/video/7645716123979238669" data-video-id="7645716123979238669" 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="♬ Hip Hop with impressive piano sound(793766) - Dusty Sky" href="https://www.tiktok.com/music/Hip-Hop-with-impressive-piano-sound-793766-6860502663327385601">♬ Hip Hop with impressive piano sound(793766) - Dusty Sky</a></section>                    </blockquote></div>                <p>Of course, this is all dependent on your cat's willingness to wear the camera. The Go 3S comes with what the company calls an "Easy clip," which you can hook onto a traditional collar. My cat didn't mind it hooked to his collar too much, but he was often so active that he would remove the collar. The camera pod itself weighs a scant 3.4 ounces, so it's pretty unobtrusive.</p><p>So, for the second go-around, I used the <a href="https://store.insta360.com/product/pet-harness-mount?c=568" target="_blank" rel="nofollow">Insta360 Pet Harness</a>, which comes in both small and medium sizes. It has two mounting points for the camera, one above and behind the shoulders, and one below the chin. </p><p>There was some initial resistance when I first put the harness on the cat, but he stopped fussing pretty quickly. </p><div class="product"><a data-dimension112="e2c41499-9518-497a-8cd2-32b992d87bb3" data-action="Deal Block" data-label="The Insta360 Pet Harness comes in both small and medium sizes; the small fits all Go cameras, while the medium fits Go cameras and the One RS." data-dimension48="The Insta360 Pet Harness comes in both small and medium sizes; the small fits all Go cameras, while the medium fits Go cameras and the One RS." data-dimension25="$53" href="https://store.insta360.com/product/pet-harness-mount?c=568" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:845px;"><p class="vanilla-image-block" style="padding-top:91.12%;"><img id="MhR6ymPRbdCkHFb3gyQ2vd" name="Pet Harness" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/MhR6ymPRbdCkHFb3gyQ2vd.png" mos="" align="middle" fullscreen="" width="845" height="770" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Insta360 Pet Harness comes in both small and medium sizes; the small fits all Go cameras, while the medium fits Go cameras and the One RS.<a class="view-deal button" href="https://store.insta360.com/product/pet-harness-mount?c=568" target="_blank" rel="nofollow" data-dimension112="e2c41499-9518-497a-8cd2-32b992d87bb3" data-action="Deal Block" data-label="The Insta360 Pet Harness comes in both small and medium sizes; the small fits all Go cameras, while the medium fits Go cameras and the One RS." data-dimension48="The Insta360 Pet Harness comes in both small and medium sizes; the small fits all Go cameras, while the medium fits Go cameras and the One RS." data-dimension25="$53">View Deal</a></p></div><p>Apart from capturing your pet's exploits, the Insta360 GO 3S is a pretty capable action camera. When attached to its action pod, it has a flip-up touchscreen, and it has other niceties like a six-axis gyroscope for motion stabilization. </p><p>In our <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-go-3s-review">Insta360 GO3S review</a>, my colleague Peter Wolinski gave it a 4.5-star rating, and even named it one of the <a href="https://www.tomsguide.com/us/best-action-cameras,review-3046.html">best action cams</a> if you're looking for a small, compact device, and don't need a high-end camera for cinema-like experiences. As much as my cat is a diva, he doesn't need the star treatment, so a lower-res video is just fine for him.</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/cameras-photography/i-saw-the-gopro-mission-1-and-this-new-action-camera-could-make-all-point-and-shoots-obsolete-heres-why"><strong>I saw the GoPro Mission 1 and this new action camera ... - Tom's Guide</strong></a></li><li><a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-6-review"><strong>Sorry GoPro, the DJI Osmo Action 6 just won the action camera war</strong></a></li><li><a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-nano-review"><strong>I tested the DJI Osmo Nano and it might just be the finest tiny action cam around — here's why</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ GoPro Mission 1 Pro review: This is the first GoPro I've been excited about in years ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/gopro-action-cameras/gopro-mission-1-pro-review</link>
                                                                            <description>
                            <![CDATA[ The GoPro Mission 1 Pro represents a change of tack for the storied but struggling action camera manufacturer. Is it enough to turn around GoPro's fortunes? ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">RTRB7UN4fDBj2RMfVrgnZA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/ySzaym78DCnoYXtJUBrpd4-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 29 May 2026 08:03:23 +0000</pubDate>                                                                                                                                <updated>Tue, 09 Jun 2026 10:32:39 +0000</updated>
                                                                                                                                            <category><![CDATA[GoPros &amp; Action Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></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/ySzaym78DCnoYXtJUBrpd4-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The GoPro Mission 1 Pro on a stone surface against a blue background]]></media:description>                                                            <media:text><![CDATA[The GoPro Mission 1 Pro on a stone surface against a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[The GoPro Mission 1 Pro on a stone surface against a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/ySzaym78DCnoYXtJUBrpd4-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you're wondering what the GoPro Mission 1 Pro and its siblings (the Mission 1 and Mission 1 ILC) are, there's both a simple answer and a complicated one.</p><p>The simple answer is that these <a href="https://www.tomsguide.com/us/best-action-cameras,review-3046.html">action cameras</a> are designed entirely with pros and hardcore creators in mind. When I say pros, I mean literal professional video producers: whether that's action sports filmmakers shooting The North Face's next YouTube documentary up in some Himalayan death zone; jobbing videographers filming anything from weddings to commercial client work; or even full-scale Hollywood movie producers.</p><p>When I say hardcore creators, I mean experienced content creators producing high-end stuff for YouTube. GoPro designed this camera not for beginners shooting TikToks, but to instead capture content creators further down their experience pipeline, when they start to get really serious. In both respects, the Mission 1 family is designed to be a portable alternative — perhaps replacement — to traditional full-sized mirrorless cameras and/or cine gear.</p><p>The complicated answer is, well... complicated. GoPro's struggles and share price are no secret, and in my opinion, it needed to innovate and pitch away from chasing mass consumer market share — it hasn't got much hope there globally, due to DJI cornering the market with its low prices. That's one for another article, though.</p><p>So, how is the Mission 1 Pro camera? And who should buy it? Find out in my full GoPro Mission 1 Pro review.</p><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-specs"><span>GoPro Mission 1 Pro review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Starting price</strong></p></td><td  ><p><a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow"><u>$699 ($599 with a GoPro sub)</u></a></p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>8K/60fps</p></td></tr><tr><td class="firstcol " ><p><strong>Sensor size</strong></p></td><td  ><p>1-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Rear screen</strong></p></td><td  ><p>2.59-inch OLED touchscreen</p></td></tr><tr><td class="firstcol " ><p><strong>Front screen</strong></p></td><td  ><p>1.4-inch LCD touchscreen</p></td></tr><tr><td class="firstcol " ><p><strong>Internal memory</strong></p></td><td  ><p>None</p></td></tr><tr><td class="firstcol " ><p><strong>MicroSD slot</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Pre-recording</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Loop recording</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Live streaming</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3, Wi-Fi, USB-C</p></td></tr><tr><td class="firstcol " ><p><strong>Water resistance</strong></p></td><td  ><p>66ft (20m)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery</strong></p></td><td  ><p>2,150 mAh</p></td></tr><tr><td class="firstcol " ><p><strong>Operating temp</strong></p></td><td  ><p>-4°F-113°F (-20°C-45°C)</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>3.15 x 2.05 x 1.74 inches (80.1 x 52.1 x 44:3 mm)</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>7.3 ounces (207g)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-price-availability"><span>GoPro Mission 1 Pro review: Price & availability</span></h2><p>The GoPro Mission 1 Pro’s only real con is its price. At <a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">$699</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£599</a>, it’s eye-wateringly expensive. You can shrink that to <a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">$599</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£509</a> with a GoPro subscription, while the lower-spec Mission 1 costs <a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">$599</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£529</a> (or <a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">$499</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£439</a> with a GoPro sub). Still not particularly appealing to general consumers.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RRj3eGVbM5SaoGJJeV5rj4" name="GoPro Mission 1 Pro-2.JPG" alt="The GoPro Mission 1 Pro side profile against a blue background" src="https://cdn.mos.cms.futurecdn.net/RRj3eGVbM5SaoGJJeV5rj4.jpg" mos="" align="middle" fullscreen="" width="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>There’s also the <a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html?option-id=CHDHW-010-master" target="_blank" rel="nofollow">$699</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£599</a> Mission 1 Pro ILS, (<a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html?option-id=CHDHW-010-master" target="_blank" rel="nofollow">$599</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£509</a> with a GoPro sub) which has a mirrorless interchangeable lens mount to fit manual focus M43 lenses. This makes it unique in the market and worth the spend if you want to do some creative filmmaking with your GoPro and M43 lenses. </p><p>But let me just return to that “not particularly appealing to general customers” bit. See, as I mentioned up top, the M1/M1 Pro aren’t really intended to be. The GoPro Hero13 Black is still on sale, and GoPro has confirmed to me that the Hero line isn’t dead. Those are for general consumers and casual creators. </p><p>Like I said, the M1 Pro is designed to be a professional alternative to a mirrorless and/or cine camera. Instead of trying to cram an A7S III or FX3 into a tight space, GoPro wants you to use the M1 Pro instead. And it's spec'd to give the kind of person who would own one of those other cameras what they need. In that light, to its target audience, the M1 Pro is actually an affordable alternative to extremely expensive pro gear.</p><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-design-controls"><span>GoPro Mission 1 Pro review: Design & controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="x7NdJoMrsoYYSZuqbQQUn4" name="GoPro Mission 1 Pro-11.JPG" alt="The GoPro Mission 1 Pro in hand, against a blue background" src="https://cdn.mos.cms.futurecdn.net/x7NdJoMrsoYYSZuqbQQUn4.jpg" mos="" align="middle" fullscreen="" width="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>At a casual glance, the Mission 1 Pro looks very much like a Hero13 Black with a large lens hood. But put ’em side by side and these are very different cameras. For starters, the M1 Pro is enormous (for an action camera). The Hero line weren’t the sveltest flagship action cameras on the market anyway — in recent years, DJI’s Osmo Action line has always been a little leaner — but the M1 Pro is an unashamed chonk. Measuring 3.15 x 2.05 x 1.74 inches and weighing over 7 ounces, this is not the best option if you’re looking for something to mount on a chin or helmet. Mainly, the size is to accommodate the new 1-inch sensor, so it figures.</p><p>The larger design has resulted in a bigger screen, which has implications for controlling the camera, as I’ll discuss below. GoPro still hasn’t managed to remove the screen bezel though. The <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-6-review">DJI Osmo Action 6</a>’s smaller bezels look much nicer. Still, the M1 Pro’s rear screen is an OLED (finally, GoPro!), which is not only much crisper and nicer to look at, but brighter and easier to use in direct sunlight than the Hero 13 Black’s LCD. The OA6 also features an OLED.</p><h4 id="mounting">Mounting</h4><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Ewr2d9QBc5xHc8b3CExNm4" name="GoPro Mission 1 Pro-13.JPG" alt="The GoPro Mission 1 Pro in hand against a blue background" src="https://cdn.mos.cms.futurecdn.net/Ewr2d9QBc5xHc8b3CExNm4.jpg" mos="" align="middle" fullscreen="" width="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>Making a return are GoPro’s trademark built-in mounting fingers, which remove the need for an adapter when mounting the camera to accessories with fork fingers — this being the vast majority of third-party accessories. </p><p>There’s also a magnetic quick release system, which is super handy for swapping accessories quickly, and is now <em>de rigueur</em> on flagship action cameras from GoPro, DJI and Insta360. </p><h4 id="controls">Controls</h4><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VEGvkx8pkyVKvMWFsGeid4" name="GoPro Mission 1 Pro-10.JPG" alt="The GoPro Mission 1 Pro rear screen against a blue background" src="https://cdn.mos.cms.futurecdn.net/VEGvkx8pkyVKvMWFsGeid4.jpg" mos="" align="middle" fullscreen="" width="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 Mission 1 Pro is the first GoPro I’ve ever tried that I didn’t dislike using. Partly, this is down to the increased screen size, finally giving the menu system the space it deserves. Secondly, there’s the menu system itself, which has been refined over the Hero 13 Black and <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/gopro-max2-review"><u>GoPro MAX 2</u></a>. </p><p>There’s still a highly detailed pull-down menu, but the touchscreen hosts (customizable) controls during live view, which have been spaced out, making them easy to hit. It’s not a revolution of GoPro’s historically cluttered menu, but a well-implemented evolution. It makes the camera <em>so much easier</em> to use than any GoPro before, even in tricky conditions and wearing gloves.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5xbBoGW3rJtJkTfAWuAdf4" name="GoPro Mission 1 Pro-6.JPG" alt="The GoPro Mission 1 Pro in hand against a blue background" src="https://cdn.mos.cms.futurecdn.net/5xbBoGW3rJtJkTfAWuAdf4.jpg" mos="" align="middle" fullscreen="" width="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 Q/Power and Record buttons are now super chunky, too, and give very assured feedback when pressed. Again, I love to see it — operating an action camera in the heat of the moment, wearing thick mountaineering or motorcycling gloves, is easier said than done. This is another small quality of life feature that makes a big difference to the camera’s daily user-friendliness.</p><h4 id="connectivity">Connectivity</h4><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="SjXFhEaDkQ72GFaThW7fd4" name="GoPro Mission 1 Pro-5.JPG" alt="The GoPro Mission 1 Pro battery port open against a blue background" src="https://cdn.mos.cms.futurecdn.net/SjXFhEaDkQ72GFaThW7fd4.jpg" mos="" align="middle" fullscreen="" width="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 Mission 1 Pro’s connectivity features are fairly run-of-the-mill. There’s the usual USB-C port, positioned annoyingly behind the battery door. That means your battery could fall out if you want to run a USB accessory. The door is also liable to getting jammed by longer fork mount screws, if you’re using one that happens to thread from the battery hatch side. </p><p>GoPro announced the M1 Pro alongside its own wireless microphone system, which will offer wireless connectivity to the camera. Obviously, this is an attempt to rival DJI’s camera/microphone ecosystem, and its versatility for content creators.</p><p>Unlike the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-5-pro-review"><u>Osmo Action 5 Pro</u></a> and 6, there’s no built-in storage, which is a bit of a letdown. The OA6’s 107GB of built in storage gives much needed headroom when recording, and also allows faster transfer speeds — important when offloading 8K footage. However, if GoPro had added internal storage, the camera would've become even larger, so on balance, I don't mind seeing it left out.</p><p>The GoPro has a MicroSD slot, though, of course. </p><h4 id="waterproofing">Waterproofing</h4><p>In order to challenge DJI’s dominance over waterproofing, GoPro has managed to waterproof the M1 and M1 Pro down to 66 feet (20m) — one foot more than the OA6. This is a big step up over the Hero13 Black, which only manages 33 feet (10m).</p><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-stabilization"><span>GoPro Mission 1 Pro review: Stabilization</span></h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/nsfr-nNUTYg" allowfullscreen></iframe></div></div><p>Unsurprisingly, the M1 Pro runs GoPro’s HyperSmooth stabilization system. No bad thing — it’s great. For a while, GoPro’s stabilization was unassailable by rivals. That’s no longer true, but it’s a level playing field now and GoPro isn’t on the back foot.</p><p>The M1 Pro’s HyperSmooth works perfectly. In most footage, even jerky walking videos, bumps are ironed out completely. It occasionally had issues with high frequency juddering, as in the motorcycle footage above, where one set of yellow painted lines on the road caused camera shake. But issues like that are rare and, for the most part, HyperSmooth does the job.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/cRZTX928nz4" allowfullscreen></iframe></div></div><p>In the footage above, I was holding the camera without any accessories and crouching while walking. It was a very shaky affair, but the M1 Pro stabilized things extremely well, especially given this was shot at the widest field of view.</p><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-video"><span>GoPro Mission 1 Pro review: Video</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Uxnv5UCdbeoEr48oXo3Zj4" name="GoPro Mission 1 Pro-8.JPG" alt="The GoPro Mission 1 Pro lens against a blue background" src="https://cdn.mos.cms.futurecdn.net/Uxnv5UCdbeoEr48oXo3Zj4.jpg" mos="" align="middle" fullscreen="" width="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 Mission 1 Pro’s video looks, well, like classic GoPro footage: professional. 4K looks gorgeous out of camera. Blues and greens in particular are lovely and vivid, in both the standard and vivid color profiles. There’s an expected level of barrel distortion from the lens, but corners remain fairly sharp even at the wide and super-wide fields of view. </p><div><blockquote><p>"As action cameras go, this sensor is delivering the best imaging on the market right now."</p></blockquote></div><p>Dynamic range has been dramatically increased over the Hero13 Black, too, thanks to the new larger sensor and GP3 processor. GoPro claims 14 stops. It's noticeable when editing, with plenty of details in shadows and fairly good control over bright highlights (there were still a few blown highlights in the footage below, though). As action cameras go, this sensor is delivering the best imaging on the market right now.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/E2hIrAWkTQo" allowfullscreen></iframe></div></div><p>If you’re shooting at 4K, there’s really not a whole lot of difference between the M1 Pro and the Hero13 Black, which also shoots lovely-looking footage. What separates the M1 Pro and marks it as a professional tool is its super versatile roster of features.</p><p>The M1 Pro shoots at up to 8K/60fps, giving it an edge over the 8K/30fps top-end of the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-6-review"><u>DJI Osmo Action 6</u></a>. Shooting in 8K allows for extra flexibility when editing, letting users crop into frame while maintaining a decent resolution, and allowing for oversampled 4K. The M1 Pro’s higher 60fps rate allows for smoother 8K than on the 30fps-capped OA6. That means you can still benefit from shooting first and cropping to 4K later, without sacrificing frame rate. Important in a professional action camera. </p><p>The M1 Pro shoots open gate (where it uses the entire uncropped sensor and resolution) at up to 8K/30fps in a 4:3 aspect ratio. Open gate means you can shoot first, frame later in the different aspect ratios you'll need for different platforms. The same recording can be cropped to 16:9 for YouTube and 9:16 for social with less risk of having to crop out important elements. Crucially, the M1 Pro’s open gate isn’t 8K/30fps locked. If you want to shoot 4:3 in 4K/60fps to reduce the file sizes, you’re still getting open gate — the camera is recording with the entire sensor then binning pixels down to 4K for extra sharp 4K out of camera. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PB7YgA5B75k4JnRJHJsbm4" name="GoPro Mission 1 Pro-12.JPG" alt="The GoPro Mission 1 Pro in hand with a camera grip, against a blue background" src="https://cdn.mos.cms.futurecdn.net/PB7YgA5B75k4JnRJHJsbm4.jpg" mos="" align="middle" fullscreen="" width="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>GoPro has given slow motion some attention, too. At 4K the M1 Pro can shoot at 240fps, while at 1080p it’ll shoot at 480fps. You’ll only get a few seconds of recording at those frame rates, though, so slow motion is there for judicious use.</p><p>But the pro features keep coming! There’s live metering, meaning real-time on-screen readouts for WB, EV, ISO and shutter; 10-bit color; high bit rate capture up to 240Mbps (or 300Mbps via Labs); HLG HDR recording and GP-Log2 with an included LUT bank for grades; and timecode sync for lining up clips to separately recorded audio in post.</p><p>And, of course, there’s GoPro Labs compatibility. Labs is an open-source, online bank of custom firmware that can be loaded onto the M1 Pro to accommodate bespoke professional requirements. Need motion detection recording for wildlife camera traps, or to run a custom settings menu? Labs is the answer.</p><h4 id="low-light">Low light</h4><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="oyUaXbd2QXa4cev2oi96QM" name="GPTempDownload.JPG" alt="A nighttime still taken from a video shot on the GoPro Mission 1 Pro" src="https://cdn.mos.cms.futurecdn.net/oyUaXbd2QXa4cev2oi96QM.jpg" mos="" align="middle" fullscreen="1" width="3840" height="2160" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/oyUaXbd2QXa4cev2oi96QM.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">A 4K still taken from a video shot in the Mission 1 Pro's low light mode at 4K/30fps. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>The M1 Pro features a 1-inch sensor, which is larger than any other action camera on the market (including the OA6) and comparable to bigger cameras like the <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review"><u>DJI Osmo Pocket 4</u></a>. A larger sensor usually means cleaner low-light footage thanks to bigger light-sensitive photosites (which translate to pixels in a digital image). To help further, the M1 Pro also features GoPro's latest GP3 image processor, for increased dynamic range and more efficient noise processing.</p><p>The result is fairly clean low light footage. As you can see in the still above and the video below, there's a noticeable loss of sharpness thanks to the GP3's noise suppression smoothing out any grain. But the processor has done a good job at the noise reduction, with very clean shadows. As action cameras go, I'd say this is as good as it gets in low light.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/lFrwkHXLTM4" allowfullscreen></iframe></div></div><p>Remember, a 1-inch sensor isn’t gonna give an APS-C or full frame <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html"><u>mirrorless camera</u></a> a run for its money, but the M1 Pro’s low light footage is still as clean as it comes for action cameras. And remember too that this camera is designed to replace a larger traditional body <em>where other factors are more important</em> (like space or waterproofing) to filmmakers — the M1 Pro's low light performance is designed to make a more palatable trade off in those situations versus a typical action camera like the Hero13 Black or Osmo Action 6.</p><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-audio"><span>GoPro Mission 1 Pro review: Audio</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vsCMogpACtRpUYw8bDNHn4" name="GoPro Mission 1 Pro-7.JPG" alt="The GoPro Mission 1 Pro front screen against a blue background" src="https://cdn.mos.cms.futurecdn.net/vsCMogpACtRpUYw8bDNHn4.jpg" mos="" align="middle" fullscreen="" width="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>Listening back to motorcycling footage shot on the M1 Pro for the first time, I was seriously impressed. As action cameras go, the four-mic array and wind reduction system in this camera are the best in the biz. </p><p>Motorcycling is a serious challenge for microphones, thanks to the wind buffeting and loud engine noises. The clip above was shot using the camera’s automatic wind reduction mode, which tailors the noise reduction so you’re getting the best balance of sound quality and noise cancellation (which can make audio sound compressed). The wind is audible, but not too distracting, and the bike’s engine noise has been well- preserved. </p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/5_pjPRPEies" allowfullscreen></iframe></div></div><p>The only camera I’ve tested with better sound is the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-x5-review">Insta360 X5</a> 360 camera. But the M1 Pro’s mics beat other standard action cams pretty confidently.</p><p>In an attempt to play DJI at its own game, GoPro is also releasing a wireless microphone system that’ll hook up to the M1 Pro seamlessly, like DJI’s mics do with its cameras. Again, it's a direct attack on the upper-end of the content creator market. I’ve yet to test these, so I can’t comment on performance yet, but it's good to see GoPro building out an ecosystem for ease of use — that's a big selling point for full-time creators.</p><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-app"><span>GoPro Mission 1 Pro review: App</span></h2><p>I’m a big fan of GoPro’s Quik app. These days, it’s reliable (even in the beta version I tested), polished, and easy to use. Hooking up your phone and M1 Pro is super simple, and there are no regular Wi-Fi connectivity bugs as is always the case with DJI gear and their companion Mimo app.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5333px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="iwq69gq9Y9x7dgqi4VSYrR" name="GP M1Pro Quik Screenshots" alt="Two screenshots of the GoPro Quik app being used to edit footage shot on the GoPro Mission 1 Pro" src="https://cdn.mos.cms.futurecdn.net/iwq69gq9Y9x7dgqi4VSYrR.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/iwq69gq9Y9x7dgqi4VSYrR.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: GoPro / Future)</span></figcaption></figure><p>Quik offers quick offloading of files to mobile, plus some basic editing features, including trimming, exposure editing, and applying basic filters or effects. Great for cutting together a few clips for a quick social video.</p><p>I really like the GPS/accelerometer-based stickers, such as the speedometer and G-counter. But I love the maps tracker, which lets you show the route of your footage in a box — so cool, especially for motovlogging videos. There’s also an altitude widget, which will be fun for any mountaineering content creators.</p><p>It’s a shame GoPro has canned the desktop version of Quik, but any pros using the M1 Pro will be editing in other software anyway, while using Quik for snappy social videos or client previews.</p><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-battery-life-thermals"><span>GoPro Mission 1 Pro review: Battery life & thermals</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RBhqfWC5bmxZweRGJijPj4" name="GoPro Mission 1 Pro-4.JPG" alt="The GoPro Mission 1 Pro Enduro 2 battery against a blue background" src="https://cdn.mos.cms.futurecdn.net/RBhqfWC5bmxZweRGJijPj4.jpg" mos="" align="middle" fullscreen="" width="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>GoPro has redeveloped its Enduro battery for the M1 Pro, resulting in the Enduro 2. And combined with the more efficient GP3 processor, Enduro 2 works wonders. The M1 Pro has the best battery life in the market, by a decent margin, too. In my standardized testing at 4K/60p, indoors, the M1 Pro achieved 133 mins. That’s a full 13 minutes longer than the Osmo Action 6. </p><p>I run my tests at 4K/60fps, as this is the resolution/frame rate most users will want to shoot action footage at, and what most cameras these days are capable of. </p><p>Importantly, the M1 Pro suffered no overheating whatsoever in that test, which was thoroughly impressive (especially as I tested the camera during a heatwave here in the U.K., where indoor temperatures were exceedingly high). The OA6 overheated twice during the same test, although in much cooler ambient conditions. GoPro's data in the table below shows overheating at 4K/30fps after 188 minutes, so at common resolutions/frame rate you have to push this camera very hard to get it to stop.</p><div ><table><caption>GoPro's claimed overheating times: M1 Pro / Enduro 2</caption><thead><tr><th class="firstcol " ><p>Aspect ratio</p></th><th  ><p>Resolution</p></th><th  ><p>Frame rate</p></th><th  ><p>With airflow</p></th><th  ><p>Without airflow</p></th></tr></thead><tbody><tr><td class="firstcol " ><p>4:3</p></td><td  ><p>8K</p></td><td  ><p>30/25/24</p></td><td  ><p>96.1</p></td><td  ><p>96.1</p></td></tr><tr><td class="firstcol " ><p>16:9</p></td><td  ><p>8K</p></td><td  ><p>60/50</p></td><td  ><p>74</p></td><td  ><p>37</p></td></tr><tr><td class="firstcol " ><p>16:9</p></td><td  ><p>4K</p></td><td  ><p>240/200</p></td><td  ><p>73.3</p></td><td  ><p>35.1</p></td></tr><tr><td class="firstcol " ><p>16:9</p></td><td  ><p>4K</p></td><td  ><p>120/100</p></td><td  ><p>105.5</p></td><td  ><p>105.5</p></td></tr><tr><td class="firstcol " ><p>4:3</p></td><td  ><p>4K</p></td><td  ><p>120/100</p></td><td  ><p>100.6</p></td><td  ><p>100.6</p></td></tr><tr><td class="firstcol " ><p>4:3</p></td><td  ><p>4K</p></td><td  ><p>30/25/24</p></td><td  ><p>188.4</p></td><td  ><p>188.4</p></td></tr><tr><td class="firstcol " ><p>16:9</p></td><td  ><p>1080p</p></td><td  ><p>30/25/24</p></td><td  ><p>315.8</p></td><td  ><p>315.8</p></td></tr></tbody></table></div><p>The M1 Pro is backwards compatible with the original Enduro battery, so any existing GoPro shooters can use their existing power cells. The new Enduro 2 is compatible with the Hero13 Black, but not older cameras (and not the MAX 2). Of course, the Enduro 2 will perform better than the Enduro 1 in the Hero13 Black, but the older processor means the gains won't match the M1 Pro's life. </p><p>Likewise, the Enduro 1 won’t last as long as the Enduro 2 when fitted in the M1 Pro, but you'll still see a battery life gain thanks to the new camera's efficient GP3 processor. To test this, I ran my standardized test at 4K/60fps with the M1 Pro and an Enduro 1 battery from my Hero13 Black. It managed 116 minutes — a whole 36 minutes longer than the same battery lasted in the Hero13 Black during the same test. The M1 Pro + Enduro 1 didn't overheat at all either, while the Hero13 Black + Enduro 1 overheated after 54 minutes. Really excellent progress!</p><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-verdict"><span>GoPro Mission 1 Pro review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ySzaym78DCnoYXtJUBrpd4" name="GoPro Mission 1 Pro-14.JPG" alt="The GoPro Mission 1 Pro on a stone surface against a blue background" src="https://cdn.mos.cms.futurecdn.net/ySzaym78DCnoYXtJUBrpd4.jpg" mos="" align="middle" fullscreen="" width="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’ve been very pleasantly surprised by the M1 Pro. It’s a demonstration of exactly what GoPro needed to do right now — push the boat a bit! The M1 Pro's core video and audio performance is excellent, but crucially, this is a camera that gives professionals a level of versatility otherwise unseen in the action camera market. The camera is designed to be a replacement for pro gear where situations demand for a small, high-powered camera with rugged credentials. </p><p>It’s pricey, sure, but if you’re a professional, it’s worth the spend to add a versatile little camera to your kit — and besides, it's cheaper than adding a second mirrorless or cine cam.</p><p>Well done, GoPro.</p><h2 class="article-body__section" id="section-gopro-mission-1-pro-review-m1-models-compared"><span>GoPro Mission 1 Pro review: M1 models compared</span></h2><div ><table><thead><tr><th class="firstcol " ><p><strong>Specs</strong></p></th><th  ><p><strong>Mission 1 Pro</strong></p></th><th  ><p><strong>Mission 1</strong></p></th><th  ><p><strong>Mission 1 Pro ILC</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>MSRP</strong></p></td><td  ><p><a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">$699</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£599</a></p></td><td  ><p><a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">$599</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£529</a></p></td><td  ><p><a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html?option-id=CHDHW-010-master" target="_blank" rel="nofollow">$699</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£599</a></p></td></tr><tr><td class="firstcol " ><p><strong>MSRP (w/ GoPro sub)</strong></p></td><td  ><p><a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">$599</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£509</a></p></td><td  ><p><a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">$499</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£439</a></p></td><td  ><p><a href="https://gopro.com/en/us/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html?option-id=CHDHW-010-master" target="_blank" rel="nofollow">$599</a> / <a href="https://gopro.com/en/gb/shop/cameras/buy/mission-1-pro/CHDHW-011-master.html" target="_blank" rel="nofollow">£509</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>1-inch</p></td><td  ><p>1-inch</p></td><td  ><p>1-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>GP3</p></td><td  ><p>GP3</p></td><td  ><p>GP3</p></td></tr><tr><td class="firstcol " ><p><strong>Lens aperture</strong></p></td><td  ><p>F2.8</p></td><td  ><p>F2.8</p></td><td  ><p>F2.8</p></td></tr><tr><td class="firstcol " ><p><strong>M43 lens fit</strong></p></td><td  ><p>No</p></td><td  ><p>No</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Max stills resolution</strong></p></td><td  ><p>50MP</p></td><td  ><p>50MP</p></td><td  ><p>50MP</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution/frame rate</strong></p></td><td  ><p>8K/60fps</p></td><td  ><p>8K/30fps</p></td><td  ><p>8K/60fps</p></td></tr><tr><td class="firstcol " ><p><strong>Open gate</strong></p></td><td  ><p>8K/30fps</p></td><td  ><p>None</p></td><td  ><p>8K/30fps</p></td></tr><tr><td class="firstcol " ><p><strong>Slow motion</strong></p></td><td  ><p>1080p/480fps; 4K/240fps</p></td><td  ><p>1080p/240fps; 4K 240fps</p></td><td  ><p>1080p/480fps; 4K/240fps</p></td></tr><tr><td class="firstcol " ><p><strong>FoVs</strong></p></td><td  ><p>SuperView; Wide; Linear</p></td><td  ><p>SuperView; Wide; Linear</p></td><td  ><p>SuperView; Wide; Linear</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>GoPro HyperSmooth</p></td><td  ><p>GoPro HyperSmooth</p></td><td  ><p>GoPro HyperSmooth</p></td></tr><tr><td class="firstcol " ><p><strong>Max bit rate (w/o Labs)</strong></p></td><td  ><p>240Mbps</p></td><td  ><p>240Mbps</p></td><td  ><p>240Mbps</p></td></tr><tr><td class="firstcol " ><p><strong>Max color bit depth</strong></p></td><td  ><p>10-bit</p></td><td  ><p>10-bit</p></td><td  ><p>10-bit</p></td></tr><tr><td class="firstcol " ><p><strong>Log</strong></p></td><td  ><p>GP-Log2</p></td><td  ><p>GP-Log2</p></td><td  ><p>GP-Log2</p></td></tr><tr><td class="firstcol " ><p><strong>HDR</strong></p></td><td  ><p>HLG HDR</p></td><td  ><p>HLG HDR</p></td><td  ><p>HLG HDR</p></td></tr><tr><td class="firstcol " ><p><strong>Rear screen</strong></p></td><td  ><p>2.59-inch OLED</p></td><td  ><p>2.59-inch OLED</p></td><td  ><p>2.59-inch OLED</p></td></tr><tr><td class="firstcol " ><p><strong>Front screen</strong></p></td><td  ><p>1.4-inch LCD</p></td><td  ><p>1.4-inch LCD</p></td><td  ><p>1.4-inch LCD</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth 5.3; Wi-Fi; USB-C</p></td><td  ><p>Bluetooth 5.3; Wi-Fi; USB-C</p></td><td  ><p>Bluetooth 5.3; Wi-Fi; USB-C</p></td></tr><tr><td class="firstcol " ><p><strong>Storage</strong></p></td><td  ><p>MicroSD</p></td><td  ><p>MicroSD</p></td><td  ><p>MicroSD</p></td></tr><tr><td class="firstcol " ><p><strong>Waterproofing</strong></p></td><td  ><p>66 foot (20m)</p></td><td  ><p>66 foot (20m)</p></td><td  ><p>66 foot (20m)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery</strong></p></td><td  ><p>2,150mAh Enduro 2</p></td><td  ><p>2,150mAh Enduro 2</p></td><td  ><p>2,150mAh Enduro 2</p></td></tr><tr><td class="firstcol " ><p><strong>Operating temperature</strong></p></td><td  ><p>-4°F-113°F (-20°C-45°C)</p></td><td  ><p>-4°F-113°F (-20°C-45°C)</p></td><td  ><p>-4°F-113°F (-20°C-45°C)</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>3.15 x 2.05 x 1.74 inches (80.1 x 52.1 x 44:3 mm)</p></td><td  ><p>3.15 x 2.05 x 1.74 inches (80.1 x 52.1 x 44:3 mm)</p></td><td  ><p>TBC</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>7.3 ounces (207g)</p></td><td  ><p>7.3 ounces (207g)</p></td><td  ><p>TBC</p></td></tr></tbody></table></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I captured these photos with the GoPro Mission 1 Pro vs iPhone 17 Pro — here’s the winner ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/gopro-action-cameras/i-captured-these-photos-with-the-gopro-mission-1-pro-vs-iphone-17-pro-heres-the-winner</link>
                                                                            <description>
                            <![CDATA[ I took the new GoPro Mission 1 Pro on a photo shootout over the weekend to see how it still-shot performance handles against the iPhone 17 Pro. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">y8xcyfUDX3rbLJJnoDCy5S</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/jbad2HT832LuHdR2CW7MsK-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 27 May 2026 07:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[GoPros &amp; Action Cameras]]></category>
                                                    <category><![CDATA[iPhones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                    <category><![CDATA[Phones]]></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/jbad2HT832LuHdR2CW7MsK-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide / John Velasco]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[GoPro Mission 1 Pro vs iPhone 17 Pro.]]></media:description>                                                            <media:text><![CDATA[GoPro Mission 1 Pro vs iPhone 17 Pro.]]></media:text>
                                <media:title type="plain"><![CDATA[GoPro Mission 1 Pro vs iPhone 17 Pro.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/jbad2HT832LuHdR2CW7MsK-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It’s a cliché for good reason, but the best camera is the one you have on you. The <a href="https://www.tomsguide.com/cameras-photography/i-saw-the-gopro-mission-1-and-this-new-action-camera-could-make-all-point-and-shoots-obsolete-heres-why">GoPro Mission 1 Pro</a> launches in a couple of days, and this <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/gopro-announces-mission-1-prices-and-point-and-shoots-are-now-in-big-trouble">$599 action camera on steroids</a> intends to shake things up. Now, as much as it’s a video-first camera in my opinion, I’m eager to see how it handles photography against the camera that’s always on me: the <a href="https://www.tomsguide.com/phones/iphones/apple-iphone-17-pro-review">iPhone 17 Pro</a>.</p><p>That’s why over this past weekend, I captured the same set of photos with both the GoPro Mission 1 Pro and iPhone 17 Pro to see which delivers the better results. Action cameras have never won me over with their still-shot performance, but the Mission 1 Pro’s 50MP 1-inch sensor isn't ordinary.</p><p>In order to make this photo shootout as fair as possible, I’m going to compare its shots against the 48MP ultrawide camera of the iPhone 17 Pro — simply because it's the lens that best matches the GoPro's sweeping perspective. However, the iPhone 17 Pro’s ultrawide shooter has a 120-degree field of view, which is narrower than the GoPro's native 159-degree view. Despite this, the iPhone 17 Pro holds an inherent utility advantage thanks to its triple-camera setup.</p><p>After pixel-peeping the results side-by-side on a monitor, I've broken down exactly how they differ — and which one I'd trust for photography.</p><h2 class="article-body__section" id="section-backyard-tree"><span>Backyard tree</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/oR8oJ42myRv3sbeDgYZG9L.jpg" alt="GoPro Mission 1 Pro camera sample." /><figcaption>GoPro Mission 1 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4mt2enLAoLnMZmfvnmwmiP.jpg" alt="Apple iPhone 17 Pro camera sample." /><figcaption>Apple iPhone 17 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>My go-to shot for testing just about everything is this tree in my backyard, simply because of the contrast in the scene. Right away, I’m drawn to the GoPro Mission 1 Pro’s superior dynamic range — mainly due to how it boosts the shadows to balance out the exposure across the entire shot. In comparison, those same shadow areas look underexposed on the iPhone 17 Pro.</p><p>Upon closer inspection, neither one has the sharp details to win over my attention, but the iPhone 17 Pro’s 24MP sample still manages to have better definition.</p><p><strong>Winner:</strong> GoPro Mission 1 Pro</p><h2 class="article-body__section" id="section-luxury-ev"><span>Luxury EV</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/PiPumNGiuQZ8PhHJYhwi5L.jpg" alt="GoPro Mission 1 Pro camera sample." /><figcaption>GoPro Mission 1 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/c4Fs9w3AsNutRgSocmtjgP.jpg" alt="Apple iPhone 17 Pro camera sample." /><figcaption>Apple iPhone 17 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>Under direct sunlight, the GoPro’s expansive field of view captures much more of the sky with the <a href="https://www.tomsguide.com/vehicle-tech/evs/2025-cadillac-optiq-ev-pros-and-cons">Cadillac Optiq</a> framed in the center. This round is tougher to judge because both devices do an excellent job capturing the EV’s finer details, from the texture of the front grille to the branding on the Continental tires.</p><p>While the iPhone 17 Pro skews a bit warmer with its color temperature, the Mission 1 Pro’s dynamic range wins out by doing a better job of boosting the shadows under the car, around the wheel wells, and the branches of the trees in the background.</p><p><strong>Winner:</strong> GoPro Mission 1 Pro</p><h2 class="article-body__section" id="section-indoor-brewery"><span>Indoor brewery</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/H54B8ZBvTvZvCsaCqg6YpK.jpg" alt="GoPro Mission 1 Pro camera sample." /><figcaption>GoPro Mission 1 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/HVgWUoobPDU4L5StMYkmWP.jpg" alt="Apple iPhone 17 Pro camera sample." /><figcaption>Apple iPhone 17 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>Inside a brewery, you would think the larger 1-inch sensor of the Mission 1 Pro would outclass its competitor, but I’m shocked by the results. The GoPro’s shot is noticeably softer and lacks definition compared to the phone.</p><p>The iPhone 17 Pro easily gains my vote thanks to its stronger contrast and superior clarity. I’m particularly impressed by how the overhead Edison lights and the graphic wall to the right both remain in sharp focus.</p><p><strong>Winner:</strong> iPhone 17 Pro</p><h2 class="article-body__section" id="section-alternate-ending"><span>Alternate Ending</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/8EeRZHxrQdQp4zMdD99MfK.jpg" alt="GoPro Mission 1 Pro camera sample." /><figcaption>GoPro Mission 1 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/w8Nvko48EfAGEQWoQ22VEP.jpg" alt="Apple iPhone 17 Pro camera sample." /><figcaption>Apple iPhone 17 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>Again, I can’t stress enough the huge disparity between these two brewery shots. The iPhone 17 Pro delivers a moodier image that perfectly captures the atmosphere, specifically because it mitigates the strong light sources before they can overpower the frame. As a result, the yellow Alternate Ending sign is properly exposed, and the textures of the wooden siding are much sharper.</p><p>The Mission 1 Pro tries to balance the exposure throughout the entire shot, but the details on the building look incredibly soft, and it completely overexposes the highlights.</p><p><strong>Winner: </strong>iPhone 17 Pro</p><h2 class="article-body__section" id="section-selfie"><span>Selfie</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/YJSpVxLCE6cShS2jX8jJ4L.jpg" alt="GoPro Mission 1 Pro camera sample." /><figcaption>GoPro Mission 1 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2waEynessJ2AftEv2cL3cP.jpg" alt="Apple iPhone 17 Pro camera sample." /><figcaption>Apple iPhone 17 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>While the ultrawide camera would probably be the last lens I’d use for a selfie on the iPhone 17 Pro, it still does a better job than the Mission 1 Pro. That’s because when I zoom in on my face and shirt, the iPhone captures far more detail and definition.</p><p>I’ll give the Mission 1 Pro credit for once again delivering better dynamic range—safeguarding the exposure in the shadowed tree branches overhead—but it ultimately falters when it comes to rendering sharp facial features.</p><p><strong>Winner:</strong> iPhone 17 Pro</p><h2 class="article-body__section" id="section-cloudy-new-york"><span>Cloudy New York</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/KzpJav4tLyHoccDwvVBXvK.jpg" alt="GoPro Mission 1 Pro camera sample." /><figcaption>GoPro Mission 1 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/bQaVDknxycuMiP5xa8QKcP.jpg" alt="Apple iPhone 17 Pro camera sample." /><figcaption>Apple iPhone 17 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>You might have trouble telling it was a cloudy day in the iPhone 17 Pro's shot because of the exposure boost it receives, but this is a prime example of Apple’s image processing working in the background to brighten the scene. Sure, the sky ends up overexposed as a result, but the brighter tone of the surrounding buildings does a much better job of grabbing my attention.</p><p>In comparison, the GoPro Mission 1 Pro’s image looks flat due to the lack of any meaningful exposure boost. Even though it's technically more representative of reality, its subdued look and softer details don't make it nearly as impressive.</p><p><strong>Winner: </strong>iPhone 17 Pro</p><h2 class="article-body__section" id="section-nighttime-backyard-tree"><span>Nighttime backyard tree</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/8rjmDLiTTjST3q9NLpnjmK.jpg" alt="GoPro Mission 1 Pro camera sample." /><figcaption>GoPro Mission 1 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/GPnAE9VXThUrdjR3zVSMDP.jpg" alt="Apple iPhone 17 Pro camera sample." /><figcaption>Apple iPhone 17 Pro.<small role="credit">Tom's Guide / John Velasco</small></figcaption></figure></figure><p>Finally, I’m back to the tree in my backyard—but this time at night under almost pitch-black conditions. Neither shot is something I’d be proud to put in a picture frame, seeing as they’re both severely underexposed due to the lack of ambient light.</p><p>Between them, however, I’m giving the nod to the GoPro Mission 1 Pro mainly because it's a tiny bit better exposed. The green grass is more noticeable in the GoPro’s shot, whereas it's completely blacked out with the iPhone 17 Pro.</p><p><strong>Winner:</strong> GoPro Mission 1 Pro</p><h2 class="article-body__section" id="section-bottom-line"><span>Bottom line</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jbW4Bo8dUKpB8dNQ7KuWwK" name="GoPro Mission 1 Pro vs iPhone 17 Pro" alt="GoPro Mission 1 Pro vs iPhone 17 Pro." src="https://cdn.mos.cms.futurecdn.net/jbW4Bo8dUKpB8dNQ7KuWwK.jpg" mos="" align="middle" fullscreen="" width="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 / John Velasco)</span></figcaption></figure><p>When it comes to handling challenging, high-contrast scenes, the GoPro Mission 1 Pro's superior dynamic range consistently pulls excellent detail out of harsh shadows — even beating the iPhone 17 Pro in broad daylight. However, its still photography leaves a bit to be desired, as it clearly lacks the crisp definition and fine detail that I crave.</p><p>That’s why the iPhone 17 Pro remains my go-to for everyday photography, especially since it's the camera that's always on me. Not only does Apple’s flagship offer a far more versatile triple-camera setup, but it’s clear that a massive amount of computational work goes into its image processing—resulting in better contrast, truer color balance, and superior detail capture.</p><p>Needless to say, I’d lean on my iPhone 17 Pro way more than the Mission 1 Pro when it comes to stills — but video is a totally different beast. Based on my time with the new GoPro so far, its video quality is already putting some of the <a href="https://www.tomsguide.com/us/best-phone-cameras,review-2272.html">best camera phones</a> on the market to shame.</p><p>Looking ahead, I’m most excited about the Mission 1 Pro ILS when it drops later this fall. Featuring an interchangeable Micro Four Thirds mount, it has the potential to truly be the one camera that can do everything.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-e4MbaW"></div>                            </div>                            <script src="https://kwizly.com/embed/e4MbaW.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/phones/iphones/iphone-fold-reportedly-facing-mass-production-delays-why-the-situation-is-not-looking-optimistic">iPhone Fold reportedly facing mass-production delays — why 'the situation is not looking optimistic'</a></li><li><a href="https://www.tomsguide.com/wellness/sleep-tech/apple-watch-vs-whoop-mg-sleep-tracker-comparison">I used the Apple Watch and Whoop to track my sleep for a year — there's a clear winner for me</a></li><li><a href="https://www.tomsguide.com/phones/iphones/15-apple-devices-expected-in-second-half-of-2026-iphone-fold-macbook-pro-m6-oled-and-a-lot-more">15 Apple devices expected in second half of 2026 — iPhone Fold, MacBook Pro M6 OLED and a lot more</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Compact cameras won't win you any photography awards but they're extremely fun, especially in the summer — and my favorites are on sale for Memorial Day ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/compact-cameras-memorial-day-2026-deals</link>
                                                                            <description>
                            <![CDATA[ Memorial Day sales are a great time to pick up a compact camera for the summer, and my favorites are now on sale, including a fantastic Fujifilm! ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">HMPqo5Jm5Mj2VnqFpmsnQ7</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/VB2w5iFCW4qmc6LopbpN24-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 24 May 2026 06:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/VB2w5iFCW4qmc6LopbpN24-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Fujifilm X half]]></media:description>                                                            <media:text><![CDATA[Fujifilm X half]]></media:text>
                                <media:title type="plain"><![CDATA[Fujifilm X half]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/VB2w5iFCW4qmc6LopbpN24-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Summertime is here and I don't know about you, but I love spending sunny evenings with snacks and friends in parks. As someone who loves capturing life's candid moments, I always take a compact or film camera with me. The lighting is great, everyone is happier, and I want to immortalize the memories I make.</p><p>If you're anything like me, you either own a compact camera already or you're on the hunt for one. You're in luck, as with <a href="https://www.tomsguide.com/sales-events/memorial-day-sales-2026">Memorial Day deals</a> being in full swing, there are some killer discounts to enjoy on portable cameras — film and digital alike. As someone who reviews the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras</a> for a living, I've found a few compact cam deals for you to take advantage of.</p><p>My favorite of the lot is the deal on the Fujifilm X half, which is <a href="https://www.amazon.com/FUJIFILM-Half-Mirrorless-Camera-Charcoal/dp/B0F9D321HT/" target="_blank" rel="nofollow">35% off right now at Amazon</a>, so it's down to just $549 from its retail price of $849. This digital/film hybrid is unlike any other I've tested so far, and at this price, it's irresistible. If you want a digital-only camera, the <a href="https://www.amazon.com/KODAK-PIXPRO-Friendly-Digital-Camera/dp/B0DY89Z83G/" target="_blank" rel="nofollow">Kodak PIXPRO C1 is down to $99 at Amazon</a>, and film fans will love the <a href="https://www.amazon.com/KODAK-Reusable-Focus-Free-Function-Improved/dp/B0DGKS6F3X/" target="_blank" rel="nofollow">Kodak Ektar H35N bundle available for $79</a>.</p><p>So, what are you waiting for? Get to shoppin'! </p><h2 class="article-body__section" id="section-quick-links"><span>Quick links</span></h2><ul><li><strong>Fujifilm X half: </strong><a href="https://www.amazon.com/FUJIFILM-Half-Mirrorless-Camera-Charcoal/dp/B0F9D321HT/" target="_blank" rel="nofollow"><strong>was $849 now $549</strong></a></li><li><strong>Kodak PIXPRO FZ45: </strong><a href="https://www.amazon.com/KODAK-Friendly-FZ45-BK-Digital-Optical/dp/B0B8PDHRWY/" target="_blank" rel="nofollow"><strong>was $129 now $99</strong></a></li><li><strong>Kodak PIXPRO FZ55: </strong><a href="https://www.amazon.com/PIXPRO-Friendly-FZ55-BK-Digital-Optical/dp/B09ZRN1N3Z/" target="_blank" rel="nofollow"><strong>was $159 now $139</strong></a></li><li><strong>Kodak PIXPRO C1: </strong><a href="https://www.amazon.com/KODAK-PIXPRO-Friendly-Digital-Camera/dp/B0DY89Z83G/" target="_blank" rel="nofollow"><strong>was $119 now $99</strong></a></li><li><strong>Kodak Ektar H35N Bundle: </strong><a href="https://www.amazon.com/KODAK-Reusable-Focus-Free-Function-Improved/dp/B0DGKS6F3X/" target="_blank" rel="nofollow"><strong>was $89 now $79</strong></a></li></ul><h2 class="article-body__section" id="section-best-compact-camera-memorial-day-deals"><span>Best compact camera Memorial Day deals</span></h2><div class="product"><a data-dimension112="2f6ba1df-396b-433d-a513-7eba4043db5c" data-action="Deal Block" data-label="Fujifilm X half" data-dimension48="Fujifilm X half" data-dimension25="$549" href="https://www.amazon.com/FUJIFILM-Half-Mirrorless-Camera-Charcoal/dp/B0F9D321HT/" 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:55.93%;"><img id="xJyTMWGaysgr5BKXfRY9xa" name="X half deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/xJyTMWGaysgr5BKXfRY9xa.jpg" mos="" align="middle" fullscreen="" width="1500" height="839" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-half-review" data-dimension112="2f6ba1df-396b-433d-a513-7eba4043db5c" data-action="Deal Block" data-label="Fujifilm X half" data-dimension48="Fujifilm X half" data-dimension25="$549">Fujifilm X half</a> is an interesting camera as it’s nothing like any other camera in the brand’s lineup. It’s small and compact and it features a 17.7MP 1-inch sensor that actually takes lovely photos. You get access to Fuji’s 13 most popular film simulation recipes and filters, and the camera has a dedicated Film Camera Mode which doesn’t let you see photos until you connect it to your phone.<a class="view-deal button" href="https://www.amazon.com/FUJIFILM-Half-Mirrorless-Camera-Charcoal/dp/B0F9D321HT/" target="_blank" rel="nofollow" data-dimension112="2f6ba1df-396b-433d-a513-7eba4043db5c" data-action="Deal Block" data-label="Fujifilm X half" data-dimension48="Fujifilm X half" data-dimension25="$549">View Deal</a></p></div><div class="product"><a data-dimension112="6e8d9eea-f365-43fc-ab61-61b875cdb1ac" data-action="Deal Block" data-label="Kodak PIXPRO FZ45" data-dimension48="Kodak PIXPRO FZ45" data-dimension25="$99" href="https://www.amazon.com/KODAK-Friendly-FZ45-BK-Digital-Optical/dp/B0B8PDHRWY/" 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:79.00%;"><img id="VtJvTiYaASyukeXrvSSgo" name="Kodak-FZ45-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/VtJvTiYaASyukeXrvSSgo.jpg" mos="" align="middle" fullscreen="" width="1500" height="1185" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-fz45-review" data-dimension112="6e8d9eea-f365-43fc-ab61-61b875cdb1ac" data-action="Deal Block" data-label="Kodak PIXPRO FZ45" data-dimension48="Kodak PIXPRO FZ45" data-dimension25="$99">Kodak PIXPRO FZ45</a> is a very easy to use compact camera that’s highly portable. Its 16.35MP sensor captures photos with a nostalgic tinge to them, and it can shoot 1080P/30fps video too. It looks great in the cherry colorway. Plus, it’s very cheap.<a class="view-deal button" href="https://www.amazon.com/KODAK-Friendly-FZ45-BK-Digital-Optical/dp/B0B8PDHRWY/" target="_blank" rel="nofollow" data-dimension112="6e8d9eea-f365-43fc-ab61-61b875cdb1ac" data-action="Deal Block" data-label="Kodak PIXPRO FZ45" data-dimension48="Kodak PIXPRO FZ45" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="043d50da-e482-46ea-9bfe-a8b71cd608a8" data-action="Deal Block" data-label="The Kodak PIXPRO FZ55 features a 5x optical zoom which can help you capture details without sacrificing image quality. This is a good compact camera for shooting on the go, thanks to its portability, and it can shoot 1080P/30fps video and capture 16MP retro-style images." data-dimension48="The Kodak PIXPRO FZ55 features a 5x optical zoom which can help you capture details without sacrificing image quality. This is a good compact camera for shooting on the go, thanks to its portability, and it can shoot 1080P/30fps video and capture 16MP retro-style images." data-dimension25="$139" href="https://www.amazon.com/PIXPRO-Friendly-FZ55-BK-Digital-Optical/dp/B09ZRN1N3Z/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:75.40%;"><img id="KnKw9RGAAXAuhPAMkPwjva" name="Pixpro-FZ55-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/KnKw9RGAAXAuhPAMkPwjva.jpg" mos="" align="middle" fullscreen="" width="1500" height="1131" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Kodak PIXPRO FZ55 features a 5x optical zoom which can help you capture details without sacrificing image quality. This is a good compact camera for shooting on the go, thanks to its portability, and it can shoot 1080P/30fps video and capture 16MP retro-style images.<a class="view-deal button" href="https://www.amazon.com/PIXPRO-Friendly-FZ55-BK-Digital-Optical/dp/B09ZRN1N3Z/" target="_blank" rel="nofollow" data-dimension112="043d50da-e482-46ea-9bfe-a8b71cd608a8" data-action="Deal Block" data-label="The Kodak PIXPRO FZ55 features a 5x optical zoom which can help you capture details without sacrificing image quality. This is a good compact camera for shooting on the go, thanks to its portability, and it can shoot 1080P/30fps video and capture 16MP retro-style images." data-dimension48="The Kodak PIXPRO FZ55 features a 5x optical zoom which can help you capture details without sacrificing image quality. This is a good compact camera for shooting on the go, thanks to its portability, and it can shoot 1080P/30fps video and capture 16MP retro-style images." data-dimension25="$139">View Deal</a></p></div><div class="product"><a data-dimension112="10699045-63e7-4f1a-aaff-a1b0c1277965" data-action="Deal Block" data-label="Kodak PIXPRO C1" data-dimension48="Kodak PIXPRO C1" data-dimension25="$99" href="https://www.amazon.com/KODAK-PIXPRO-Friendly-Digital-Camera/dp/B0DY89Z83G/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:900px;"><p class="vanilla-image-block" style="padding-top:133.33%;"><img id="fHbenGQJ4FURFke6JkTGFT" name="collections-product-2-kodak_pixpro_c1-2026-04-16T13-08-55" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/fHbenGQJ4FURFke6JkTGFT.jpg" mos="" align="middle" fullscreen="" width="900" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-c1-compact-camera-review" data-dimension112="10699045-63e7-4f1a-aaff-a1b0c1277965" data-action="Deal Block" data-label="Kodak PIXPRO C1" data-dimension48="Kodak PIXPRO C1" data-dimension25="$99">Kodak PIXPRO C1</a> is highly compact and portable so you can take it anywhere, and it can capture 1080P/60fps footage. There’s a dedicated Macro mode that works well, too. For those who love taking selfies, the camera features a flip-up selfie screen for self-portraits and group shots.<a class="view-deal button" href="https://www.amazon.com/KODAK-PIXPRO-Friendly-Digital-Camera/dp/B0DY89Z83G/" target="_blank" rel="nofollow" data-dimension112="10699045-63e7-4f1a-aaff-a1b0c1277965" data-action="Deal Block" data-label="Kodak PIXPRO C1" data-dimension48="Kodak PIXPRO C1" data-dimension25="$99">View Deal</a></p></div><div class="product"><a data-dimension112="9c0dc49e-791e-41d2-a0c4-c80969403224" data-action="Deal Block" data-label="Film cameras can help you live in the moment as you can't see the photos (and obsess over details) until you get them developed, and the Kodak Ektar H35N is a fantastic choice. It's a half-frame camera so it takes two photos on one frame. It's highly portable too, and this bundle comes with a protective case as well as filters." data-dimension48="Film cameras can help you live in the moment as you can't see the photos (and obsess over details) until you get them developed, and the Kodak Ektar H35N is a fantastic choice. It's a half-frame camera so it takes two photos on one frame. It's highly portable too, and this bundle comes with a protective case as well as filters." data-dimension25="$79" href="https://www.amazon.com/KODAK-Reusable-Focus-Free-Function-Improved/dp/B0DGKS6F3X/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1499px;"><p class="vanilla-image-block" style="padding-top:100.07%;"><img id="RxAHdXc4oSx9XYVWtEZAdD" name="Ektar-H35N-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/RxAHdXc4oSx9XYVWtEZAdD.jpg" mos="" align="middle" fullscreen="" width="1499" height="1500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Film cameras can help you live in the moment as you can't see the photos (and obsess over details) until you get them developed, and the Kodak Ektar H35N is a fantastic choice. It's a half-frame camera so it takes two photos on one frame. It's highly portable too, and this bundle comes with a protective case as well as filters.<a class="view-deal button" href="https://www.amazon.com/KODAK-Reusable-Focus-Free-Function-Improved/dp/B0DGKS6F3X/" target="_blank" rel="nofollow" data-dimension112="9c0dc49e-791e-41d2-a0c4-c80969403224" data-action="Deal Block" data-label="Film cameras can help you live in the moment as you can't see the photos (and obsess over details) until you get them developed, and the Kodak Ektar H35N is a fantastic choice. It's a half-frame camera so it takes two photos on one frame. It's highly portable too, and this bundle comes with a protective case as well as filters." data-dimension48="Film cameras can help you live in the moment as you can't see the photos (and obsess over details) until you get them developed, and the Kodak Ektar H35N is a fantastic choice. It's a half-frame camera so it takes two photos on one frame. It's highly portable too, and this bundle comes with a protective case as well as filters." data-dimension25="$79">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-exEVKW"></div>                            </div>                            <script src="https://kwizly.com/embed/exEVKW.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/i-just-tested-kodaks-new-compact-camera-and-my-favorite-feature-is-one-i-didnt-know-i-needed">I just tested Kodak’s new compact camera — and it's my favorite retro gadget of the year</a></li><li><a href="https://www.tomsguide.com/cameras-photography/you-always-need-two-cameras-in-your-backpack-and-one-of-them-should-cost-under-usd100-heres-why">You always need two cameras in your backpack (and one of them should cost under $100) — here’s why</a></li><li><a href="https://www.tomsguide.com/cameras-photography/i-tested-the-fujifilm-x-half-for-two-weeks-and-some-might-think-this-one-feature-is-a-gimmick-but-its-my-favorite">I tested the Fujifilm X half for two weeks — and some might think this one feature is a gimmick but it’s my favorite</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I test drones for a living and my top-rated quadcopters are on sale for Memorial Day — including a 5-star DJI ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/drones/memorial-day-2026-drone-deals</link>
                                                                            <description>
                            <![CDATA[ There's never a bad time to buy a drone, but this weekend's Memorial Day sales are an excellent time to do so, and I've handpicked the best deals for you. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">jCfWGkRyhHWDeGTYviBHCL</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/uUpdwEUNu2GHCSaEyXzu6K-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 23 May 2026 08:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Drones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/uUpdwEUNu2GHCSaEyXzu6K-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[DJI Mavic 4 Pro drone]]></media:description>                                                            <media:text><![CDATA[DJI Mavic 4 Pro drone]]></media:text>
                                <media:title type="plain"><![CDATA[DJI Mavic 4 Pro drone]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/uUpdwEUNu2GHCSaEyXzu6K-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>There's never a bad time to buy a drone — take it from someone who reviews these nifty gadgets for a living. I've spent over 900 hours testing the <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">best drones</a>, and I get very excited anytime my top picks receive price cuts. With <a href="https://www.tomsguide.com/sales-events/memorial-day-sales-2026">Memorial Day deals</a> in full swing, now's a great time to grab a quadcopter for yourself. After all, few things in life are better than capturing the sights from a bird's-eye view.</p><p>Whether you're just a beginner or an experienced pilot, there's a drone for everyone. For example, the <a href="https://www.amazon.com/DJI-Mini-Fly-More-Combo/dp/B07FTRYVT5/" target="_blank" rel="nofollow">DJI Neo Fly More Combo is down to just $295 at Amazon</a>, and it comes with extra batteries and a controller. This is an excellent pick for those just starting out. "But what if I've been flying drones for years?" I got you. The <a href="https://www.amazon.com/DJI-Flagship-Tri-Camera-Hasselblad-Transmission/dp/B0DS2GVT2G/" target="_blank" rel="nofollow">DJI Mavic 4 Pro Fly More Combo is available for $2,749 at Amazon</a> — and I gave this drone a 5-star rating!</p><p>Of course, non-DJI drones are on sale too, like the <a href="https://www.amazon.com/Potensic-Drones-Transmission-Vertical-Shooting/dp/B0FSL3XKX3/" target="_blank" rel="nofollow">Potenstic Atom 2 which is down to $519 at Amazon</a>, and the <a href="https://www.amazon.com/Antigravity-Immersive-Beginners-Obstacle-Avoidance/dp/B0G6WRQNQL/" target="_blank" rel="nofollow">Antigravity A1 which is 20% off</a> right now. Here are all the Memorial Day deals that are actually worth your money, handpicked by a registered drone pilot.</p><h2 class="article-body__section" id="section-quick-links"><span>Quick links</span></h2><ul><li><strong>DJI Mavic 4 Pro Fly More Combo: </strong><a href="https://www.amazon.com/DJI-Flagship-Tri-Camera-Hasselblad-Transmission/dp/B0DS2GVT2G/" target="_blank" rel="nofollow"><strong>was $2,899 now $2,749</strong></a></li><li><strong>DJI Neo Fly More Combo: </strong><a href="https://www.amazon.com/DJI-Mini-Fly-More-Combo/dp/B07FTRYVT5/" target="_blank" rel="nofollow"><strong>was $349 now $295</strong></a></li><li><strong>DJI Avata 2 Fly More Combo: </strong><a href="https://www.amazon.com/DJI-Batteries-Immersive-Experience-Acrobatics/dp/B0CS6HVW6P/" target="_blank" rel="nofollow"><strong>was $1,199 now $1,019</strong></a></li><li><strong>Potensic Atom 2 Fly More Combo: </strong><a href="https://www.amazon.com/Potensic-Drones-Transmission-Vertical-Shooting/dp/B0FSL3XKX3/" target="_blank" rel="nofollow"><strong>was $549 now $519</strong></a></li><li><strong>HoverAir X1: </strong><a href="https://www.amazon.com/gp/aw/d/B0CQJTHM5B/" target="_blank" rel="nofollow"><strong>was $339 now $288</strong></a></li><li><strong>Antigravity A1: </strong><a href="https://www.amazon.com/Antigravity-Immersive-Beginners-Obstacle-Avoidance/dp/B0G6WRQNQL/" target="_blank" rel="nofollow"><strong>was $1,599 now $1,279</strong></a></li></ul><h2 class="article-body__section" id="section-best-memorial-day-drone-deals"><span>Best Memorial Day drone deals</span></h2><div class="product"><a data-dimension112="bd4dea12-cb95-464e-8c92-81aefa6b2a2f" data-action="Deal Block" data-label="DJI Mavic 4 Pro" data-dimension48="DJI Mavic 4 Pro" data-dimension25="$2749" href="https://www.amazon.com/DJI-Flagship-Tri-Camera-Hasselblad-Transmission/dp/B0DS2GVT2G/" 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:85.70%;"><img id="Kvkti4eq8co7VcNrx6QL6k" name="DJIM4P-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Kvkti4eq8co7VcNrx6QL6k.jpg" mos="" align="middle" fullscreen="" width="1000" height="857" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/drones/dji-mavic-4-pro-review" data-dimension112="bd4dea12-cb95-464e-8c92-81aefa6b2a2f" data-action="Deal Block" data-label="DJI Mavic 4 Pro" data-dimension48="DJI Mavic 4 Pro" data-dimension25="$2749">DJI Mavic 4 Pro</a> improves on its predecessor by introducing 6K/60fps video, 100MP stills powered by a Hasselblad camera, and a brand new RC Pro 2 controller that makes flying a breeze. With a long flight time and 5-star performance across the board, it’s the ideal drone for commercial and personal use.<a class="view-deal button" href="https://www.amazon.com/DJI-Flagship-Tri-Camera-Hasselblad-Transmission/dp/B0DS2GVT2G/" target="_blank" rel="nofollow" data-dimension112="bd4dea12-cb95-464e-8c92-81aefa6b2a2f" data-action="Deal Block" data-label="DJI Mavic 4 Pro" data-dimension48="DJI Mavic 4 Pro" data-dimension25="$2749">View Deal</a></p></div><div class="product"><a data-dimension112="31a0a427-7099-4f3e-95b6-b17d9ce3509d" data-action="Deal Block" data-label="DJI Neo" data-dimension48="DJI Neo" data-dimension25="$295" href="https://www.amazon.com/DJI-Mini-Fly-More-Combo/dp/B07FTRYVT5/" 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="ftPxFuwMzcEkXSjmxkyvDA" name="DJI Neo" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/ftPxFuwMzcEkXSjmxkyvDA.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>A lightweight, compact and minimalistic drone, the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-neo-review" data-dimension112="31a0a427-7099-4f3e-95b6-b17d9ce3509d" data-action="Deal Block" data-label="DJI Neo" data-dimension48="DJI Neo" data-dimension25="$295">DJI Neo</a> is a fantastic drone that can shoot 4K footage at 30fps, take detailed stills, and remain stable in tricky conditions. Easy to control with the new RC-N3 controller or the DJI Fly app on a smartphone, the Neo makes drone photography more accessible thanks to its low price point.<a class="view-deal button" href="https://www.amazon.com/DJI-Mini-Fly-More-Combo/dp/B07FTRYVT5/" target="_blank" rel="nofollow" data-dimension112="31a0a427-7099-4f3e-95b6-b17d9ce3509d" data-action="Deal Block" data-label="DJI Neo" data-dimension48="DJI Neo" data-dimension25="$295">View Deal</a></p></div><div class="product"><a data-dimension112="269ceaca-f912-4b30-97b6-5c597bf263d0" data-action="Deal Block" data-label="DJI Avata 2" data-dimension48="DJI Avata 2" data-dimension25="$1019" href="https://www.amazon.com/DJI-Batteries-Immersive-Experience-Acrobatics/dp/B0CS6HVW6P/" 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:56.07%;"><img id="zF5rpNba3z5QQZ3QjsxNXm" name="61VTNpkhkEL._AC_SL1500_.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/zF5rpNba3z5QQZ3QjsxNXm.jpg" mos="" align="middle" fullscreen="" width="1500" height="841" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>This deal is for FPV enthusiasts who want an easy-to-navigate drone that can get into tight spaces. The <a href="https://www.tomsguide.com/cameras-photography/drones/i-flew-the-dji-avata-2-for-an-entire-month-heres-what-i-wish-i-knew-beforehand" data-dimension112="269ceaca-f912-4b30-97b6-5c597bf263d0" data-action="Deal Block" data-label="DJI Avata 2" data-dimension48="DJI Avata 2" data-dimension25="$1019">DJI Avata 2</a> is a robust, sturdy drone with propeller guards to protect it when flying close to other objects. It offers greater creative control over how the drone flies which unlocks unique perspectives and dynamic angles that traditional drones can’t achieve.<a class="view-deal button" href="https://www.amazon.com/DJI-Batteries-Immersive-Experience-Acrobatics/dp/B0CS6HVW6P/" target="_blank" rel="nofollow" data-dimension112="269ceaca-f912-4b30-97b6-5c597bf263d0" data-action="Deal Block" data-label="DJI Avata 2" data-dimension48="DJI Avata 2" data-dimension25="$1019">View Deal</a></p></div><div class="product"><a data-dimension112="6fb3d85c-621b-40a9-96ab-0dd66959046f" data-action="Deal Block" data-label="Potensic Atom 2" data-dimension48="Potensic Atom 2" data-dimension25="$519" href="https://www.amazon.com/Potensic-Drones-Transmission-Vertical-Shooting/dp/B0FSL3XKX3/" 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:78.13%;"><img id="QEqNG4iNnxL2KBjVQKhum8" name="Potensic-Atom-2-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/QEqNG4iNnxL2KBjVQKhum8.jpg" mos="" align="middle" fullscreen="" width="1500" height="1172" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-2-review" data-dimension112="6fb3d85c-621b-40a9-96ab-0dd66959046f" data-action="Deal Block" data-label="Potensic Atom 2" data-dimension48="Potensic Atom 2" data-dimension25="$519">Potensic Atom 2</a> is a fantastic drone for budget-conscious pilots. It records smooth 4K/30fps video and captures detailed 48MP stills. It features advanced and accurate AI subject tracking, and comes with a new, easy to use controller and app — it’s also lightweight yet sturdy. This drone gives DJI a serious run for its money.<a class="view-deal button" href="https://www.amazon.com/Potensic-Drones-Transmission-Vertical-Shooting/dp/B0FSL3XKX3/" target="_blank" rel="nofollow" data-dimension112="6fb3d85c-621b-40a9-96ab-0dd66959046f" data-action="Deal Block" data-label="Potensic Atom 2" data-dimension48="Potensic Atom 2" data-dimension25="$519">View Deal</a></p></div><div class="product"><a data-dimension112="bf41109b-a852-4140-89a2-cb8dec3f4cf6" data-action="Deal Block" data-label="enjoyed using it a lot" data-dimension48="enjoyed using it a lot" data-dimension25="$288" href="https://www.amazon.com/gp/aw/d/B0CQJTHM5B/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1441px;"><p class="vanilla-image-block" style="padding-top:92.23%;"><img id="DNC8WM5nWsq28ydhTF8jj" name="HoverAir-X1-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DNC8WM5nWsq28ydhTF8jj.jpg" mos="" align="middle" fullscreen="" width="1441" height="1329" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The HoverAir X1 is a great selfie drone as it can fly without a controller or a smartphone — all you need to do is press the buttons on its body, and it'll record short-form videos. My colleague <a href="https://www.tomsguide.com/features/sorry-dji-this-pocketable-folding-drone-is-the-most-fun-ive-had-flying-in-a-long-time" data-dimension112="bf41109b-a852-4140-89a2-cb8dec3f4cf6" data-action="Deal Block" data-label="enjoyed using it a lot" data-dimension48="enjoyed using it a lot" data-dimension25="$288">enjoyed using it a lot</a> and appreciated its surprisingly good obstacle avoidance and subject tracking features such as follow, rocket, and more. This combo includes a storage bag, a charging hub, and a USB cable.<a class="view-deal button" href="https://www.amazon.com/gp/aw/d/B0CQJTHM5B/" target="_blank" rel="nofollow" data-dimension112="bf41109b-a852-4140-89a2-cb8dec3f4cf6" data-action="Deal Block" data-label="enjoyed using it a lot" data-dimension48="enjoyed using it a lot" data-dimension25="$288">View Deal</a></p></div><div class="product"><a data-dimension112="6b75fe7a-f420-45fa-932e-12868cf1dacf" data-action="Deal Block" data-label="Antigravity A1" data-dimension48="Antigravity A1" data-dimension25="$1279" href="https://www.amazon.com/Antigravity-Immersive-Beginners-Obstacle-Avoidance/dp/B0G6WRQNQL/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:884px;"><p class="vanilla-image-block" style="padding-top:70.81%;"><img id="NGdR9g2gcxuLzKBw6dG9Nh" name="Antigravity-A1-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/NGdR9g2gcxuLzKBw6dG9Nh.jpg" mos="" align="middle" fullscreen="" width="884" height="626" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/drones/antigravity-a1-review" data-dimension112="6b75fe7a-f420-45fa-932e-12868cf1dacf" data-action="Deal Block" data-label="Antigravity A1" data-dimension48="Antigravity A1" data-dimension25="$1279">Antigravity A1</a> is fitted with tech you’ll find in the Insta360's action and 360° cameras, and it lets you capture every angle possible. Footage is shot in 8K/30fps, enabling you to maintain 4K in 16x9 when reframing in post — and this footage is packed with detail. 55MP stills look great too. Flying the drone is easy (and immersive) with the Vision goggles and Grip controller.<a class="view-deal button" href="https://www.amazon.com/Antigravity-Immersive-Beginners-Obstacle-Avoidance/dp/B0G6WRQNQL/" target="_blank" rel="nofollow" data-dimension112="6b75fe7a-f420-45fa-932e-12868cf1dacf" data-action="Deal Block" data-label="Antigravity A1" data-dimension48="Antigravity A1" data-dimension25="$1279">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-Ww1LaX"></div>                            </div>                            <script src="https://kwizly.com/embed/Ww1LaX.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/drones/i-test-drones-for-a-living-heres-what-i-think-of-reddits-top-recommendations-for-beginners">I test drones for a living — here’s what I think of Reddit’s top recommendations for beginners</a></li><li><a href="https://www.tomsguide.com/us/best-drones,review-2412.html">I’ve spent over 900 hours testing all the best drones: here are my top picks</a></li><li><a href="https://www.tomsguide.com/us/best-drones,review-2412.html">Best Picks</a></li><li><a href="https://www.tomsguide.com/cameras-photography/drones/my-top-rated-drones-for-the-summer">Summer is the best time for aerial photography — here are my top-rated drones for the perfect shots</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Canon EOS R6 V and the Sony A7R VI dropped this week — both look incredible but here’s the one I’m most excited for (and why) ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/the-canon-eos-r6-v-and-the-sony-a7r-vi-dropped-this-week-both-look-incredible-but-heres-the-one-im-most-excited-for-and-why</link>
                                                                            <description>
                            <![CDATA[ The Canon EOS R6 V and the Sony A7R VI are here, and both cameras sound powerful and offer best-in-class features. But there's one camera I'm more excited for. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">UAJpDjwJcWMv6KVG2RhuF6</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/RNWmMtFfENFb7ADz2uZgbG-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 17 May 2026 10:45:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Mirrorless Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/RNWmMtFfENFb7ADz2uZgbG-1280-80.jpg">
                                                            <media:credit><![CDATA[Canon / Sony]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Canon EOS R6 V and Sony A7R VI]]></media:description>                                                            <media:text><![CDATA[Canon EOS R6 V and Sony A7R VI]]></media:text>
                                <media:title type="plain"><![CDATA[Canon EOS R6 V and Sony A7R VI]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/RNWmMtFfENFb7ADz2uZgbG-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It's been quite an eventful few week for cameras. Insta360 launched a <a href="https://www.tomsguide.com/cameras-photography/groovy-baby-insta360-launches-latest-go-3s-bundle-inspired-by-retro-film-photography">Go 3S Retro Bundle</a>, we saw the <a href="https://www.tomsguide.com/cameras-photography/i-saw-the-gopro-mission-1-and-this-new-action-camera-could-make-all-point-and-shoots-obsolete-heres-why">GoPro Mission 1 lineup</a>, and on Wednesday May 13th, Sony and Canon dropped a camera each — what a time to be alive! Sony and Canon's  announcements came out of the blue, with teasers posted on their respective social channels either a week or just a few days prior to the launches.</p><p>Canon has expanded its V-series lineup of video-first cameras with the new EOS R6 V, while the Sony A7R VI succeeds the four-years-old <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a7r-v-review">A7R V</a>. Sony's offering sounds like the ultimate hybrid that builds on its predecessor's success, while Canon's latest camera looks like it could be the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless camera</a> for video.</p><p>Needless to say, I'm excited and thrilled. As someone who reviews cameras for a living and loves photographing her surroundings, I'm intrigued by both cameras. But is there one I'm excited about more than the other? Well, that's what we're here to find out.</p><h2 id="a-quick-rundown-of-specs">A quick rundown of specs</h2><div ><table><thead><tr><th class="firstcol " ><p><strong>Specs</strong></p></th><th  ><p><strong>Sony A7R VI</strong></p></th><th  ><p>Canon EOS R6 V</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow">$4,498</a> / <a href="https://www.lcegroup.co.uk/New/Sony-A7R-VI-Mirrorless-Camera-Body_229893.html" target="_blank" rel="nofollow">£4,399</a></p></td><td  ><p><a href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow">$2,499</a> / <a href="https://www.lcegroup.co.uk/New/Canon-EOS-R6-V-Mirrorless-Camera-Body_229883.html" target="_blank" rel="nofollow">£2,399</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>66.8MP Stacked Exmor RS BSI full frame</p></td><td  ><p>32.5MP CMOS full frame</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>BIONZ XR2</p></td><td  ><p>DIGIC X</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Up to 8.5-stops IBIS</p></td><td  ><p>Up to 8.5-stops IBIS</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Real-time Recognition AF+</p></td><td  ><p>Dual Pixel CMOS  II</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>0.64-type Quad-XGA OLED, 9.44M dots, 120fps refresh rate</p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3.2-inch 4-axis multi-angle LCD, 2.1M dots</p></td><td  ><p>3-inch vari-angle, 1.62M dots</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>8K/30fps and 4K/120fps</p></td><td  ><p>7K/60fps RAW and 7K/30fps Open Gate</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>30fps</p></td><td  ><p>40fps</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>600 shots (viewfinder)</p></td><td  ><p>640 shots</p></td></tr></tbody></table></div><h2 id="who-are-the-canon-eos-r6-v-and-sony-a7r-vi-for">Who are the Canon EOS R6 V and Sony A7R VI for?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1813px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="7mpPzNdW9KgsGjShTnvAH4" name="Canon-EOSR6V-1" alt="Canon EOS R6 V" src="https://cdn.mos.cms.futurecdn.net/7mpPzNdW9KgsGjShTnvAH4.jpg" mos="" align="middle" fullscreen="" width="1813" height="1020" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Canon)</span></figcaption></figure><p>Both the Canon EOS R6 V and the Sony A7R VI have different target audiences — but with some overlap. The A7R VI sets a new gold standard for high-resolution full frame cameras with its 66.8MP sensor and 30fps RAW burst capabilities, which make it great for all genres of photography, including wildlife, sports, and portrait. It also features 8K/30fps video so it sounds like a fantastic all-rounder.</p><p>Bolstering the A7R VI's position as a powerful hybrid camera is its OLED viewfinder with a 120fps refresh rate and 9.44M-dot resolution. You also get a 3.2-inch 4-axis LCD monitor with a 2.1M-dot resolution. Oh, and 8.5-stops of in-body image stabilization (IBIS) which means you can shoot at very slow shutter speeds and still walk away with a usable photo.</p><div class="product"><a data-dimension112="dc23baf3-0c00-47fe-b096-6a7fa2c51761" data-action="Deal Block" data-label="The Sony A7R VI is a professional-grade hybrid camera that utilizes a newly developed 66.8MP sensor with 30fps RAW burst shooting and can shoot 8K/30fps video. It also offers up to 8.5-stops of IBIS, and packs a high-res EVF as well as a 4-axis LCD monitor." data-dimension48="The Sony A7R VI is a professional-grade hybrid camera that utilizes a newly developed 66.8MP sensor with 30fps RAW burst shooting and can shoot 8K/30fps video. It also offers up to 8.5-stops of IBIS, and packs a high-res EVF as well as a 4-axis LCD monitor." data-dimension25="$4498" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="WZxeQLkZhoTgmrzRbKRu5L" name="A7RVI-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WZxeQLkZhoTgmrzRbKRu5L.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony A7R VI is a professional-grade hybrid camera that utilizes a newly developed 66.8MP sensor with 30fps RAW burst shooting and can shoot 8K/30fps video. It also offers up to 8.5-stops of IBIS, and packs a high-res EVF as well as a 4-axis LCD monitor.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow" data-dimension112="dc23baf3-0c00-47fe-b096-6a7fa2c51761" data-action="Deal Block" data-label="The Sony A7R VI is a professional-grade hybrid camera that utilizes a newly developed 66.8MP sensor with 30fps RAW burst shooting and can shoot 8K/30fps video. It also offers up to 8.5-stops of IBIS, and packs a high-res EVF as well as a 4-axis LCD monitor." data-dimension48="The Sony A7R VI is a professional-grade hybrid camera that utilizes a newly developed 66.8MP sensor with 30fps RAW burst shooting and can shoot 8K/30fps video. It also offers up to 8.5-stops of IBIS, and packs a high-res EVF as well as a 4-axis LCD monitor." data-dimension25="$4498">View Deal</a></p></div><p>And what about the EOS R6 V? It's the second R-V body Canon has introduced, and it joins the excellent <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r50-v-review">Canon EOS R50 V</a>. The 'V' version of the EOS R6 is also the <em>fourth </em>EOS R6 body in existence — and as the 'V' suggests, it's a video-first take on Canon's hybrid camera. There's no viewfinder so you must rely on the camera's 3-inch vari-angle screen and its 1.62M-dot resolution. The EOS R6 V offers 7K/60fps RAW and 7K/30fps Open Gate video capabilities, both of which make it ideal for content creation.</p><div class="product"><a data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="The Canon EOS R6 V is a compact video-first full frame camera for content creators, and it features a 32.5MP sensor, 7K/60fps RAW and 7K/30fps Open Gate capabilities. It utilizes Canon's excellent Dual Pixel CMOS II autofocus system and up to 8.5-stops of IBIS too." data-dimension48="The Canon EOS R6 V is a compact video-first full frame camera for content creators, and it features a 32.5MP sensor, 7K/60fps RAW and 7K/30fps Open Gate capabilities. It utilizes Canon's excellent Dual Pixel CMOS II autofocus system and up to 8.5-stops of IBIS too." data-dimension25="$2499" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="p5QzTEnfDQHjKyd6QWp2E4" name="EOSR6V-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/p5QzTEnfDQHjKyd6QWp2E4.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Canon EOS R6 V is a compact video-first full frame camera for content creators, and it features a 32.5MP sensor, 7K/60fps RAW and 7K/30fps Open Gate capabilities. It utilizes Canon's excellent Dual Pixel CMOS II autofocus system and up to 8.5-stops of IBIS too.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow" data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="The Canon EOS R6 V is a compact video-first full frame camera for content creators, and it features a 32.5MP sensor, 7K/60fps RAW and 7K/30fps Open Gate capabilities. It utilizes Canon's excellent Dual Pixel CMOS II autofocus system and up to 8.5-stops of IBIS too." data-dimension48="The Canon EOS R6 V is a compact video-first full frame camera for content creators, and it features a 32.5MP sensor, 7K/60fps RAW and 7K/30fps Open Gate capabilities. It utilizes Canon's excellent Dual Pixel CMOS II autofocus system and up to 8.5-stops of IBIS too." data-dimension25="$2499">View Deal</a></p></div><p>In addition to that, you get 40fps continuous shooting which is... surprising, and something I wasn't really expecting in a video-first camera. While this camera sounds like it would be peerless for video, I'm sure it will be ideal for capturing photos of birds in flight too with its 32.5MP CMOS sensor and best-in-class Dual Pixel CMOS II autofocus system. And did I mention it packs up to 8.5-stops of IBIS, just like the A7R VI? Yep, it's almost got it all.</p><h2 id="the-one-i-m-most-excited-for-and-why">The one I'm most excited for (and why)</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:800px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9kA4YPjZVRCKMM4vA8LnF4" name="Canon-EOSR6V-2" alt="Canon EOS R6 V" src="https://cdn.mos.cms.futurecdn.net/9kA4YPjZVRCKMM4vA8LnF4.jpg" mos="" align="middle" fullscreen="" width="800" height="450" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Canon)</span></figcaption></figure><p>I'd be lying if I said I wasn't intrigued by the Canon EOS R6 V. It features the same 32.5MP sensor as the Canon EOS R6 Mark III. As a video-first iteration of the series, it packs an active cooling fan — which you'll most certainly need if you're shooting 7K/30fps Open Gate... or even 4K/60fps, really, especially outdoors.</p><p>With the EOS R6 Mark III and its predecessors, battery life while recording video was always a sticking point, but this time around, Canon's going for the "best battery life" crown. The company claims the EOS R6 V can record for two hours or more as long as the built-in fan is toggled on, the overheat limit is set to high, and the camera's on a tripod.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5831px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="4Bq6cCVFc3T9jxaswjWAGL" name="Sony-A7RVI-1" alt="Sony A7R VI" src="https://cdn.mos.cms.futurecdn.net/4Bq6cCVFc3T9jxaswjWAGL.jpg" mos="" align="middle" fullscreen="" width="5831" height="3280" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>While all of this sounds great (and I'm looking forward to test the camera for my full review), the EOS R6 V isn't the one I'm most excited about — it's the Sony A7R VI. The A7R VI packs a newly developed 66.8MP sensor and enables you to shoot RAW photos at 30fps. This will certainly be a boon for wildlife photographers such as myself.</p><p>I loved the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II</a> when I tested it and it's the one camera I've thought about buying myself time and again. I captured some of my best photos on it, and being able to make extreme crops was a walk in the park thanks to the 50MP resolution. But 66.8MP sounds even better, and the sensor's stacked nature delivers faster readout speeds and minimizes rolling shutter distortion. Then you've got the new BIONZ XR2 processor for 30fps blackout-free bursts, so you don't lose track of your subject because of, well, black screens between shots.</p><p>The A7R VI also borrows Auto subject detection mode from the A1 II, so you don't need to manually change the subject you're photographing — handy for anyone who captures a variety of subject. I loved this on the A1 II as I photograph birds, animals and (occasionally) humans and I can't wait to see how it holds up on the A7R VI.</p><h2 id="a-new-trick-up-its-sleeve">A new trick up its sleeve</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1645px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="P5su7FzHCBigT75dh2KceZ" name="Sony-A7RVI-2" alt="Sony A7R VI" src="https://cdn.mos.cms.futurecdn.net/P5su7FzHCBigT75dh2KceZ.jpg" mos="" align="middle" fullscreen="" width="1645" height="926" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Sony)</span></figcaption></figure><p>In addition to all the goodies the Sony A7R VI offers, it also features a new battery pack: the 2,670mAh NP-SA100. Sony says that with this battery pack, the A7R VI is CIPA-rated for 600 shots with the viewfinder or 710 shots with the monitor. Its predecessor, the A7R V, is CIPA-rated for 440 shots, so Sony's made some strides.</p><p>CIPA testing is done under strict conditions and the number is often conservative, so it'll be interesting to see how the battery holds up in real-world use.</p><p>I think it's going to be interesting to see how both the A7R VI and the Canon EOS R6 V perform in practice as, in theory, they both sound great in their own right. I'm obviously not pitting both cameras against one another. That would be unfair as they have different target audiences (and there's quite a bit of a price difference). Needless to say, though, it's an exciting time for photographers and camera enthusiasts. I can't wait to get my hands on both cameras and put them through their paces!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eJkPyW"></div>                            </div>                            <script src="https://kwizly.com/embed/eJkPyW.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/ive-got-two-masters-degrees-and-the-sony-a1-ii-makes-me-feel-stupid-heres-why">I tested the Sony A1 II for 2 weeks — and its autofocus results blew my mind</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-just-tested-the-canon-eos-r5-mark-ii-to-photograph-wildlife-and-this-one-feature-has-changed-how-i-take-photos-forever">I just tested the Canon EOS R5 Mark II to photograph wildlife — and this one feature has changed how I take photos forever</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-wildlife-camera-first-impressions">‘Few wildlife cameras come close to this’: why the OM System OM-1 Mark II is perfect for photographing birds in flight and other animals</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I bought the Kodak Charmera and it's not supposed to look good, but I still think everyone should have one when taking a trip with friends ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/i-bought-the-kodak-charmera-and-its-not-supposed-to-look-good-but-i-still-think-everyone-should-have-one-when-taking-a-trip-with-friends</link>
                                                                            <description>
                            <![CDATA[ I bought the TikTok and Instagram viral Kodak Charmera, and it's changed the way I take photos when I'm with my friends. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">bekZ9NSFA2HjzwgGLuKvFU</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/pRYRi8a3u9FiFSZEPZxPvD-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 16 May 2026 11:15:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></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/pRYRi8a3u9FiFSZEPZxPvD-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A Kodak Charmera keychain camera]]></media:description>                                                            <media:text><![CDATA[A Kodak Charmera keychain camera]]></media:text>
                                <media:title type="plain"><![CDATA[A Kodak Charmera keychain camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/pRYRi8a3u9FiFSZEPZxPvD-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you've read any of my articles on the original <a href="https://www.tomsguide.com/cameras-photography/my-favorite-retro-compact-camera-just-had-a-major-glow-up-but-should-you-buy-the-original-or-the-new-model">Camp Snap</a> or the <a href="https://www.tomsguide.com/cameras-photography/ive-been-taking-this-screen-free-digital-camera-everywhere-with-me-heres-why-i-prefer-it-to-my-phone">Camp Snap CS-Pro</a>, you'll know that I'm a sucker for a gimmicky camera. I enjoy the fact that they're cheap enough that you don't really have to worry about damaging them, and because of that, I can pass them around to my friends. I'm never worried about them hitting the wrong buttons or messing around with settings.</p><p>That's why I always make sure that when I'm out and about with friends, I have a digital camera with me — and a cheap one at that. It may not give the same level of quality as your phone or your <a href="https://www.tomsguide.com/cameras-photography/canon-powershot-g7-x-mark-iii-still-worth-it-in-2026">Canon G7X Mark III</a>, but it means you can put your phone away, switch off from all notifications, and be present in the moment. </p><p>So, when I saw the <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a> on my social media feed, I knew I needed one. It's a point-and-shoot that's small enough to fit on your keychain — it's so convenient, there's literally no excuse for leaving it at home. </p><div class="product"><a data-dimension112="fb14230f-fc89-4ed8-b41a-2cc2e375612c" data-action="Deal Block" data-label="The Kodak Charmera is a highly portable keychain camera featuring a loop that can hook onto your keys or your bag. It is extremely easy to use, and it can record 1080P video at 30fps. While image quality isn't the best (1.6MP only), that's not the point, as it's just a fun little toy camera." data-dimension48="The Kodak Charmera is a highly portable keychain camera featuring a loop that can hook onto your keys or your bag. It is extremely easy to use, and it can record 1080P video at 30fps. While image quality isn't the best (1.6MP only), that's not the point, as it's just a fun little toy camera." data-dimension25="$34" href="https://www.amazon.com/KODAK-Recording-Simulation-Megapixels-Beginners/dp/B0FKBDT8FR" 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="Za4m6Ke9gei6nfS2S6a3MZ" name="kodak-charmera-keychain-digital-camera-b-bda82ae2-fc1a-4a4d-980f-1e04288cc638.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/Za4m6Ke9gei6nfS2S6a3MZ.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Kodak Charmera is a highly portable keychain camera featuring a loop that can hook onto your keys or your bag. It is extremely easy to use, and it can record 1080P video at 30fps. While image quality isn't the best (1.6MP only), that's not the point, as it's just a fun little toy camera.<a class="view-deal button" href="https://www.amazon.com/KODAK-Recording-Simulation-Megapixels-Beginners/dp/B0FKBDT8FR" target="_blank" rel="nofollow" data-dimension112="fb14230f-fc89-4ed8-b41a-2cc2e375612c" data-action="Deal Block" data-label="The Kodak Charmera is a highly portable keychain camera featuring a loop that can hook onto your keys or your bag. It is extremely easy to use, and it can record 1080P video at 30fps. While image quality isn't the best (1.6MP only), that's not the point, as it's just a fun little toy camera." data-dimension48="The Kodak Charmera is a highly portable keychain camera featuring a loop that can hook onto your keys or your bag. It is extremely easy to use, and it can record 1080P video at 30fps. While image quality isn't the best (1.6MP only), that's not the point, as it's just a fun little toy camera." data-dimension25="$34">View Deal</a></p></div><h2 id="camera-quality">Camera 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="5oo6iBxFnAtjF5F4QnYPgE" name="Kodak_charmera 2.JPG" alt="A Kodak Charmera keychain camera" src="https://cdn.mos.cms.futurecdn.net/5oo6iBxFnAtjF5F4QnYPgE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 know what you're probably thinking. Why would you use a 1.6MP camera over, say, the 24MP from the front camera of your iPhone? Because it's fun, of course!</p><p>I'm not claiming that the Kodak Charmera is going to give you crisp and clear images, because it's not. But what I <em>am</em> claiming is that you and your friends are going to have buckets of fun using it. </p><p>As you can see from the images, they are lo-fi, and the camera struggles with the highlights of the image. So not the best choice for a professional photo shoot. But that's not the point.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/soz8Un8WhKS9BV8XcsaHpV.jpg" alt="Pictures taken using the Kodak Charmera" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/XQksUpL5WED69sVNB8epoV.jpg" alt="Pictures taken using the Kodak Charmera" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Lb83Sx9gsUHi6kYRAGxnV.jpg" alt="Pictures taken using the Kodak Charmera" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/qc77paosawitSnqc7qeUqV.jpg" alt="Pictures taken using the Kodak Charmera" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jDKdEixpWJ5QAhpEMxsWqV.jpg" alt="Pictures taken using the Kodak Charmera" /><figcaption><small role="credit">Tom's Guide</small></figcaption></figure></figure><p>I went out to grab some pizza with friends and took the camera with me, attached to my keys. We passed it around the table, snapping a bunch of pictures while we waited for our food. It turned out to be loads of fun!</p><p>Because the controls are so easy to use, everybody can have a go. You just look through the tiny viewfinder or on the digital screen and hit the big button on top to take the picture — so simple. </p><h2 id="it-s-a-mystery">It's a mystery!</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="c2PATj3j7bpaPW6svmfwZE" name="Kodak_charmera 9.JPG" alt="A Kodak Charmera keychain camera" src="https://cdn.mos.cms.futurecdn.net/c2PATj3j7bpaPW6svmfwZE.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 reason this camera went viral is that it's a total mystery which color you're going to get. The camera comes in a blind box, so you don't know which of the seven different colors you're going to receive until you open the box.</p><p>This concept adds to the idea that the camera is nothing more than a toy. And in truth, that's how I'm treating it. It put the whimsy back into taking pictures.</p><p>Sure, it looks cute hanging on your keys or dangling on the outside of your bag, but ultimately, it's there so you can enjoy snapping silly pictures. </p><p>I think most people are in desperate need of less screen time, and if a gimmicky camera is the way to do that and be more present, then I think everybody needs a Kodak Charmera. </p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review"><strong>‘A $30 camera meant for fun, not serious photography’: Kodak Charmera review</strong></a></li><li><a href="https://www.tomsguide.com/cameras-photography/ive-been-taking-this-screen-free-digital-camera-everywhere-with-me-heres-why-i-prefer-it-to-my-phone"><strong>I’ve been taking this screen-free digital camera everywhere with me — here’s why I prefer it to my phone</strong></a></li><li><a href="https://www.tomsguide.com/cameras-photography/my-favorite-retro-compact-camera-just-had-a-major-glow-up-but-should-you-buy-the-original-or-the-new-model"><strong>My favorite retro compact camera just got a major glow-up — but should you buy the original or the new model?</strong></a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Don't splurge on the brand new Sony A7R VI just yet — I'd get its awesome predecessor for $900 off right now ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/dont-splurge-on-the-brand-new-sony-a7r-vi-just-yet-id-get-its-awesome-predecessor-for-usd900-off-right-now</link>
                                                                            <description>
                            <![CDATA[ Sony dropped the A7R VI earlier this week and in light of that, its predecessor has received a massive price cut. You can save $900 on the A7R V today. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">UAME6CBduqrDjKJZGJs2yA</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/fgN7q2yQR3sZdPkF8wcrZE-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 15 May 2026 10:55:52 +0000</pubDate>                                                                                                                                <updated>Fri, 15 May 2026 14:50:29 +0000</updated>
                                                                                                                                            <category><![CDATA[Mirrorless Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/fgN7q2yQR3sZdPkF8wcrZE-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A person holding a Sony A7R V mirrorless camera]]></media:description>                                                            <media:text><![CDATA[A person holding a Sony A7R V mirrorless camera]]></media:text>
                                <media:title type="plain"><![CDATA[A person holding a Sony A7R V mirrorless camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/fgN7q2yQR3sZdPkF8wcrZE-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Earlier this week, Sony revealed the latest addition to its extensive lineup of mirrorless cameras: the Sony A7R VI. It succeeds the four-year-old <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a7r-v-review">Sony A7R V</a>, and on paper, it sounds like a dream. It packs a newly designed 66.8MP sensor, making it the highest resolution full-frame camera today. 30fps RAW burst, a next-gen processor, Auto subject detection, a high-res EVF and a 4-axis monitor — it's got it all.</p><p>But that doesn't mean the A7R V is going anywhere (for now). The A7R V is still one of the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a> you can buy today, and in light of its successor now being a reality, it's cheaper than ever. That's right, the <a href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow">A7R V body is currently $900 off at Adorama</a>, making it a little more affordable at $3,298 (rather than its retail price of $4,198).</p><div class="product"><a data-dimension112="e97493bf-fe17-4291-9576-2a2051734fc8" data-action="Deal Block" data-label="The Sony A7R V is an epic full frame hybrid with enough resolution to retire on. Stills are, as you can imagine, beautiful, combined with Sony’s vaunted AF and stabilization. And there’s some chunky video specs, such as 8K/30fps, to boot." data-dimension48="The Sony A7R V is an epic full frame hybrid with enough resolution to retire on. Stills are, as you can imagine, beautiful, combined with Sony’s vaunted AF and stabilization. And there’s some chunky video specs, such as 8K/30fps, to boot." data-dimension25="$3298" href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1500px;"><p class="vanilla-image-block" style="padding-top:68.93%;"><img id="DBkLKC6xyDRtt2gFTkSHsd" name="Sony A7RV deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/DBkLKC6xyDRtt2gFTkSHsd.jpg" mos="" align="middle" fullscreen="" width="1500" height="1034" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony A7R V is an epic full frame hybrid with enough resolution to retire on. Stills are, as you can imagine, beautiful, combined with Sony’s vaunted AF and stabilization. And there’s some chunky video specs, such as 8K/30fps, to boot.<a class="view-deal button" href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow" data-dimension112="e97493bf-fe17-4291-9576-2a2051734fc8" data-action="Deal Block" data-label="The Sony A7R V is an epic full frame hybrid with enough resolution to retire on. Stills are, as you can imagine, beautiful, combined with Sony’s vaunted AF and stabilization. And there’s some chunky video specs, such as 8K/30fps, to boot." data-dimension48="The Sony A7R V is an epic full frame hybrid with enough resolution to retire on. Stills are, as you can imagine, beautiful, combined with Sony’s vaunted AF and stabilization. And there’s some chunky video specs, such as 8K/30fps, to boot." data-dimension25="$3298">View Deal</a></p></div><p>When we reviewed the A7R V, we awarded it a 4.5-star rating along with the coveted Editor's Choice Award — and for good reason, too. This camera's got everything you need to create stellar content, from high-res stills to cinematic videos. The A7R V packs a 61MP sensor that enables you to make extreme crops, and powerful autofocus as well as strong high dynamic range performance make it a winner.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/zPiwzpQUcCKS3SjhLJakEh.jpg" alt="A black and white photograph of a greyhound lying in a bed looking at the camera" /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/TZwAc7qhAPfbjBFmfUxVkG.jpg" alt="A photo of a green flower with a caterpillar on the inside of one petal." /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/koPvtgvkw5b2RzVYwiALV5.jpg" alt="A seagull standing on the ground" /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/X5uaBKB2gqC4TD32ftS8S5.jpg" alt="Three greyhounds running" /><figcaption><small role="credit">Peter Wolinski</small></figcaption></figure></figure><p>The A7R V features up to 8-stops of in-body image stabilization (IBIS) which enables you to shoot at slow shutter speeds, making low-light photography a walk in the park. The colors it captures are nothing short of beautiful, and if you want a powerful hybrid camera, it can shoot 8K/30fps video too.</p><p>What makes the A7R V a sheer delight to use is its 0.64-inch Quad-XGA OLED electronic viewfinder (EVF) with a resolution of 9.44M dots. That means it's bright, extremely sharp, and great for extensive focus checking of those highly detailed 61MP images. Ergonomics are peerless, thanks to the protruding sculpted grip and a body that's heavy enough that it feels sturdy but not <em>too </em>heavy that it feels unwieldy.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="swGKMYPRqDbWisx7f7Ao2N" name="Sony A7RV" alt="The Sony A7RV against a blue background" src="https://cdn.mos.cms.futurecdn.net/swGKMYPRqDbWisx7f7Ao2N.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future Photo Studios)</span></figcaption></figure><p>I'm very excited to get my hands on the new Sony A7R VI because it's got some drool-worthy specs — but so does the older A7R V. I personally <a href="https://www.tomsguide.com/cameras-photography/you-dont-need-to-upgrade-your-camera-just-because-a-new-one-has-come-out-here-are-5-older-cameras-that-still-take-stunning-photos">don't believe everyone needs to get the latest camera as old cameras still take stunning photos</a>, and the A7R V is one of them. The <a href="https://www.adorama.com/sony-alpha-a7r-v-mirrorless-camera/p/isoa7r5" target="_blank" rel="nofollow">A7R V body is $900 off at Adorama right now</a> which make it a steal, and as it's a limited time offer, I'd grab this deal with both hands and run.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a7r-v-review">Sony A7R V review: The ultimate Sony hybrid</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/ive-got-two-masters-degrees-and-the-sony-a1-ii-makes-me-feel-stupid-heres-why">I tested the Sony A1 II for 2 weeks — and its autofocus results blew my mind</a></li><li><a href="https://www.tomsguide.com/cameras-photography/if-i-had-to-start-my-camera-setup-from-scratch-id-pick-these-5-cameras-and-drones-heres-why">If I had to start my camera setup from scratch, I’d pick these 5 cameras and drones — here’s why</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Groovy, baby: Insta360 launches latest Go 3S bundle, blending 4K tech with retro film soul ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/groovy-baby-insta360-launches-latest-go-3s-bundle-inspired-by-retro-film-photography</link>
                                                                            <description>
                            <![CDATA[ Insta360 has introduced its newest iteration of the Go 3S camera, the Go 3S Retro, aiming to bring the charm of nostalgic film photography into 4K video. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ks48UEREPYP5JBnZEMsK87</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/YakHHQme4thW26N4ZjUJ8R-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 14 May 2026 12:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ lucy.scotting@futurenet.com (Lucy Scotting) ]]></author>                    <dc:creator><![CDATA[ Lucy Scotting ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/UC9ncAYxkmJ5ipHEyX44ri.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Lucy Scotting is a staff writer for Tom’s Guide Australia, primarily covering lifestyle, streaming and internet-related news. Lucy started her career writing for HR and staffing industry publications, with articles covering emerging tech, business and finance.&lt;/p&gt;&lt;p&gt;In her spare time, Lucy can be found watching sci-fi movies, working on her dystopian fiction novel (in progress since 2017) or hanging out with her dog, Fletcher. If she’s not found doing any of the above, she’s likely on her next adventure to a new city, country, or continent. &lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/YakHHQme4thW26N4ZjUJ8R-1280-80.jpg">
                                                            <media:credit><![CDATA[Insta360]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Insta360 Go 3S Retro]]></media:description>                                                            <media:text><![CDATA[Insta360 Go 3S Retro]]></media:text>
                                <media:title type="plain"><![CDATA[Insta360 Go 3S Retro]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/YakHHQme4thW26N4ZjUJ8R-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Insta360 has just introduced a special edition of its thumb-sized Go 3S camera, and its blend of modern 4K tech with nostalgic style is intriguing.</p><p>Dubbed the Insta360 Go 3S Retro Bundle, the new edition brings the nostalgic film aesthetic into 4K video. This new bundle arrives two years after the original Go 3S launched in June 2024. In our review, we gave the Go 3S a <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-go-3s-review">tidy 4.5 stars</a>, and found it packed outstanding features — namely 4K video recording, superb battery life, and cool new functions accessible via its smartphone app.</p><p>The biggest differences (so far) between the Go 3S and Go 3S Retro are the new add-ons that come with the tiny cam. The most obvious is the new Retro viewfinder, which pairs with the standalone camera to create a handheld shooting experience. The waist-level viewfinder lets you frame shots without looking at the screen and comes with a built-in mirror for quick selfies. </p><p>The camera also has new vintage-inspired filters and accessories for hands-free capturing. As with the Go 3S, the camera itself is magnetic, allowing you to mount it easily to many Insta360 mounts (like the included strap and magnet pendant in the bundle). You can also pair it with the accompanying smartphone app for easy editing and remote control of the camera. </p><p>Pricewise, the bundle is very similar to the Go 3S, costing $300 / £279 / AU$469.99 for 64GB and $320 / £299 / AU$499.99 for 128GB. The bundles are available now in Canvas White and Classic Red on Amazon and Insta360. If you already own the Go 3S and want to nab the viewfinder, you can pick it up separately for $48 / £46 / AU$80. </p><h2 id="revival-of-retro-inspired-cameras">Revival of retro-inspired cameras</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5000px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="gZjsjgQjGF6FRgDr4mKQuQ" name="16 copy" alt="Insta360 Go 3S Retro" src="https://cdn.mos.cms.futurecdn.net/gZjsjgQjGF6FRgDr4mKQuQ.jpg" mos="" align="middle" fullscreen="" width="5000" height="2813" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Insta360)</span></figcaption></figure><p>The new vintage-inspired Insta360 Go 3S joins a long line of other cameras revisiting the retro aesthetic. From the <a href="https://www.tomsguide.com/cameras-photography/polaroid-go-gen-2-review">Polaroid Go</a> range to the <a href="https://www.tomsguide.com/cameras-photography/camp-snap-screen-free-digital-camera-review">screen-free Camp Snap</a> and every cute camera in between, the retro aesthetic shots are back, and I'm all for it. </p><p>After picking up the <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a> earlier this year and documenting so much of my everyday life on it, I'm super keen to get my hands on the Insta360 Go 3S Retro. Sure, the specs aren't that different from the original model, but it's a fun new take on the already impressively teeny camera.</p><p>Are you hopping on the retro-cam bandwagon? Let me know in the comments below.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ You don’t need to upgrade your camera just because a new one has come out — here are 5 older cameras that still take stunning photos ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/you-dont-need-to-upgrade-your-camera-just-because-a-new-one-has-come-out-here-are-5-older-cameras-that-still-take-stunning-photos</link>
                                                                            <description>
                            <![CDATA[ Just because a new camera is on the block doesn't mean you need to buy it because old cameras still take beautiful photos. Here are my top picks. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">4CF5WzqjFEbrETzeTeKdDZ</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/VoPiCteW2hEXQ3DN8UvKNC-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 13 May 2026 08:30:00 +0000</pubDate>                                                                                                                                <updated>Fri, 15 May 2026 13:45:09 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/VoPiCteW2hEXQ3DN8UvKNC-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A Sony camera]]></media:description>                                                            <media:text><![CDATA[A Sony camera]]></media:text>
                                <media:title type="plain"><![CDATA[A Sony camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/VoPiCteW2hEXQ3DN8UvKNC-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I've loved cameras for as long as I can remember, so I'm very lucky that I now get to test them day in and day out. As someone who reviews the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras</a>, I'm well aware of how (most) brands love releasing at least two or three new snappers per year. I'm also well aware that not every "upgrade" is really worth "upgrading" to. Because if you have, say, the <a href="https://www.tomsguide.com/reviews/sony-a7-iv">Sony A7 IV</a> and it works perfectly fine, why would you <em>need </em>to get the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-spent-a-day-shooting-on-the-new-sony-a7-v-here-are-my-first-impressions-spoiler-alert-its-great">Sony A7 V</a>? </p><p>Of course, if you can afford to upgrade your camera every year, good for you. But for those who can't due to budget constraints, you don't <em>need </em>to do that. Or if you're new to the world of photography and are overwhelmed by the many expensive choices around you, don't fret: I'm here to tell you that older cameras do the job just fine.</p><p>Whether you're looking for a casual camera for vlogging or a fast camera for capturing wildlife and sports, you're spoilt for choice. I've handpicked my 5 favorite and somewhat-older cameras that have either been succeeded by new models or are rumored to be soon. There's certainly some truth in the old saying: old is gold.</p><div class="product"><a data-dimension112="4c0b247a-d334-4bd8-93fb-6fddd8dcf7dc" data-action="Deal Block" data-label="Sony ZV-1F" data-dimension48="Sony ZV-1F" data-dimension25="$448" href="https://www.adorama.com/sony-zv-1f-vlogging-camera-20-mp-4k/p/isozv1fb" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1023px;"><p class="vanilla-image-block" style="padding-top:109.97%;"><img id="YSoFf9ZeQCzZ2qBEFBMg4B" name="ZV-1F" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/YSoFf9ZeQCzZ2qBEFBMg4B.jpg" mos="" align="middle" fullscreen="" width="1023" height="1125" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Stepping up from a smartphone? The <a href="https://www.tomsguide.com/cameras-photography/sony-zv-1f-review" data-dimension112="4c0b247a-d334-4bd8-93fb-6fddd8dcf7dc" data-action="Deal Block" data-label="Sony ZV-1F" data-dimension48="Sony ZV-1F" data-dimension25="$448">Sony ZV-1F</a> is a great vlogging camera that’s portable and doesn’t break the bank. It shoots lovely 4K/30P footage, can shoot in S-Log 3 to capture a wider dynamic range, and comes with handy vlogging features. Stills taken are good too, as is the built-in mic.<a class="view-deal button" href="https://www.adorama.com/sony-zv-1f-vlogging-camera-20-mp-4k/p/isozv1fb" target="_blank" rel="nofollow" data-dimension112="4c0b247a-d334-4bd8-93fb-6fddd8dcf7dc" data-action="Deal Block" data-label="Sony ZV-1F" data-dimension48="Sony ZV-1F" data-dimension25="$448">View Deal</a></p></div><div class="product"><a data-dimension112="2980b845-1720-4de5-8827-cfcbdef60051" data-action="Deal Block" data-label="PowerShot Elph 360 HS A / IXUS 285 HS A" data-dimension48="PowerShot Elph 360 HS A / IXUS 285 HS A" data-dimension25="$379" href="https://www.adorama.com/canon-powershot-elph-360-hs-20-2-mp-digital-camera/p/icae360hsbk" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1067px;"><p class="vanilla-image-block" style="padding-top:73.38%;"><img id="HVh9VC9QsbkbYgdUwkmhtf" name="Screenshot 2026-03-05 at 11.23.33" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/HVh9VC9QsbkbYgdUwkmhtf.png" mos="" align="middle" fullscreen="" width="1067" height="783" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The <a href="https://www.tomsguide.com/cameras-photography/canon-powershot-elph-360-hs-a-ixus-285-hs-a-review" data-dimension112="2980b845-1720-4de5-8827-cfcbdef60051" data-action="Deal Block" data-label="PowerShot Elph 360 HS A / IXUS 285 HS A" data-dimension48="PowerShot Elph 360 HS A / IXUS 285 HS A" data-dimension25="$379">PowerShot Elph 360 HS A / IXUS 285 HS A</a> is a refresh of Canon’s 10-year-old compact camera, featuring its predecessor’s 20.2MP CMOS sensor, digital image stabilization, and FHD/30fps video recording. It offers dedicated creative modes for some, well, creative freedom while shooting, and you get a handy 12x optical zoom to capture zoomed-in shots without losing much detail.<a class="view-deal button" href="https://www.adorama.com/canon-powershot-elph-360-hs-20-2-mp-digital-camera/p/icae360hsbk" target="_blank" rel="nofollow" data-dimension112="2980b845-1720-4de5-8827-cfcbdef60051" data-action="Deal Block" data-label="PowerShot Elph 360 HS A / IXUS 285 HS A" data-dimension48="PowerShot Elph 360 HS A / IXUS 285 HS A" data-dimension25="$379">View Deal</a></p></div><div class="product"><a data-dimension112="043adffa-c6bb-45d9-b500-23f612b1cf8b" data-action="Deal Block" data-label="Canon EOS R5 Mark II" data-dimension48="Canon EOS R5 Mark II" data-dimension25="$2799" href="https://www.adorama.com/canon-eos-r5-mirrorless-camera-body-only/p/car5" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1446px;"><p class="vanilla-image-block" style="padding-top:94.88%;"><img id="L4fqXiWC85XSneS3GjkhVH" name="EOS-R5-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/L4fqXiWC85XSneS3GjkhVH.png" mos="" align="middle" fullscreen="" width="1446" height="1372" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Although it has been succeeded by the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r5-mark-ii-review" data-dimension112="043adffa-c6bb-45d9-b500-23f612b1cf8b" data-action="Deal Block" data-label="Canon EOS R5 Mark II" data-dimension48="Canon EOS R5 Mark II" data-dimension25="$2799">Canon EOS R5 Mark II</a>, the OG EOS R5 still makes for an excellent investment. Packing a 45MP sensor and the DIGIC X processor capable of 20fps continuous burst, the EOS R5 is suited to high-speed photography, and it can record 8K/30fps video too, making it a great hybrid choice. Boasting one of the best autofocus systems in the biz, the EOS R5 is one of the best workhorses for wedding, sports, wildlife, and portrait photography.<a class="view-deal button" href="https://www.adorama.com/canon-eos-r5-mirrorless-camera-body-only/p/car5" target="_blank" rel="nofollow" data-dimension112="043adffa-c6bb-45d9-b500-23f612b1cf8b" data-action="Deal Block" data-label="Canon EOS R5 Mark II" data-dimension48="Canon EOS R5 Mark II" data-dimension25="$2799">View Deal</a></p></div><div class="product"><a data-dimension112="9c2eee22-a624-47f3-a1ab-fbb3828753d3" data-action="Deal Block" data-label="Sony A7 IV review" data-dimension48="Sony A7 IV review" data-dimension25="$1998" href="https://www.adorama.com/sony-alpha-a7-iv-mirrorless-camera-body-34mp-full-frame-4k/p/isoa7m4" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1113px;"><p class="vanilla-image-block" style="padding-top:68.37%;"><img id="N26X5FMJ85Hk9n5jjjpNmg" name="Sony A7 IV.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/N26X5FMJ85Hk9n5jjjpNmg.jpg" mos="" align="middle" fullscreen="" width="1113" height="761" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>In our <a href="https://www.tomsguide.com/reviews/sony-a7-iv" data-dimension112="9c2eee22-a624-47f3-a1ab-fbb3828753d3" data-action="Deal Block" data-label="Sony A7 IV review" data-dimension48="Sony A7 IV review" data-dimension25="$1998">Sony A7 IV review</a>, we loved the 33MP full-frame sensor and the CFExpress slot for fast write speeds when shooting at high speeds, or for high data video capture. Obviously, there's the fantastic Sony AF and IBIS, too. This really is an advanced pro tool. I wouldn't worry too much about the A7 V taking up the spotlight, as it also features a 33MP sensor — and I think it will be a <em>while</em> before the A7 IV fades into obscurity.<a class="view-deal button" href="https://www.adorama.com/sony-alpha-a7-iv-mirrorless-camera-body-34mp-full-frame-4k/p/isoa7m4" target="_blank" rel="nofollow" data-dimension112="9c2eee22-a624-47f3-a1ab-fbb3828753d3" data-action="Deal Block" data-label="Sony A7 IV review" data-dimension48="Sony A7 IV review" data-dimension25="$1998">View Deal</a></p></div><div class="product"><a data-dimension112="c52a65c4-a36b-49a5-ba15-6a7d32b6408b" data-action="Deal Block" data-label="Fujifilm X-H2S" data-dimension48="Fujifilm X-H2S" data-dimension25="$2699" href="https://www.adorama.com/fujifilm-x-h2s-mirrorless-camera-body-26mp/p/ifjxh2s" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1650px;"><p class="vanilla-image-block" style="padding-top:72.73%;"><img id="HeyRvfmeVTHxARwJtt7kS7" name="Fujifilm X-H2S.jpg" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/HeyRvfmeVTHxARwJtt7kS7.jpg" mos="" align="middle" fullscreen="" width="1650" height="1200" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>Released in 2022, the <a href="https://www.tomsguide.com/reviews/fujifilm-xh2s" data-dimension112="c52a65c4-a36b-49a5-ba15-6a7d32b6408b" data-action="Deal Block" data-label="Fujifilm X-H2S" data-dimension48="Fujifilm X-H2S" data-dimension25="$2699">Fujifilm X-H2S</a> is still worth it in 2026. This is the ultimate APS-C camera. Thanks to falling prices, it makes more sense now than ever to get one for yourself. This camera still packs the latest and greatest Fuji has to offer, and will be more than enough for all but the most demanding pros for a long time to come. 40fps burst, 26MP images, 5-axis IBIS, and CFExpress support make it an absolute powerhouse.<a class="view-deal button" href="https://www.adorama.com/fujifilm-x-h2s-mirrorless-camera-body-26mp/p/ifjxh2s" target="_blank" rel="nofollow" data-dimension112="c52a65c4-a36b-49a5-ba15-6a7d32b6408b" data-action="Deal Block" data-label="Fujifilm X-H2S" data-dimension48="Fujifilm X-H2S" data-dimension25="$2699">View Deal</a></p></div><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-O6P1de"></div>                            </div>                            <script src="https://kwizly.com/embed/O6P1de.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/i-test-compact-and-film-cameras-for-a-living-and-here-are-my-top-12-recommendations-for-the-summer">I test compact and film cameras for a living, and here are my top 12 recommendations for the summer</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/ive-been-using-my-fujifilm-camera-for-almost-a-year-and-these-are-the-time-saving-photography-hacks-i-swear-by">I’ve been using my Fujifilm camera for almost a year and these are the time-saving photography hacks I swear by</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-wildlife-camera-first-impressions">‘Few wildlife cameras come close to this’: why the OM System OM-1 Mark II is perfect for photographing birds in flight and other animals</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested the DJI RS 5 — and it's a super charged gimbal that content creators will love ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/dji-rs-5-gimbal-review</link>
                                                                            <description>
                            <![CDATA[ With its faster setup and integrated tracking, the DJI RS5 is the best gimbal for mirrorless cameras. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">54t8Cj5sgV2CrioyoL8s9P</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/YisQtfeeNeNTdoSHrN7xjR-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 09 May 2026 09:15:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ Paul.antill@futurenet.com (Paul Antill) ]]></author>                    <dc:creator><![CDATA[ Paul Antill ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/7MyNkF6oJfDnS9kZG658oC.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Paul Antill is a Video Producer and Content Creator at Tom&#039;s Guide, specializing in video/audio recording, smart home technology, and laptops. His passion lies in making tech coverage not just informative but also fun and accessible to everyone answering the question “Why does this new product or feature matter to me?” Paul has been a tech host and video producer since 2019 where he has also covered major tech and gaming events. His love for tech and video began on his&amp;nbsp;&lt;a href=&quot;https://www.youtube.com/@paulAntill&quot;&gt;&lt;strong&gt;YouTube channel&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;where for the low price of one subscription he shares head-to-head comparisons and clever ways to make the most of your gear. You can also see the behind-the-scenes and the magic that goes into our Tom’s Guide videos over on his&amp;nbsp;&lt;a href=&quot;https://www.instagram.com/thepaulantill/&quot;&gt;&lt;strong&gt;Instagram&lt;/strong&gt;&lt;/a&gt;. Paul graduated from the Mason School of Business at William &amp;amp; Mary, Williamsburg, VA, with a degree in Marketing and Business Analytics. When he&#039;s not on camera, you’ll probably still spot him behind one doing street photography and videography. Back home he’s probably in a heated Overwatch 2 match with his friends.&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/YisQtfeeNeNTdoSHrN7xjR-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[DJI RS 5]]></media:description>                                                            <media:text><![CDATA[DJI RS 5]]></media:text>
                                <media:title type="plain"><![CDATA[DJI RS 5]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/YisQtfeeNeNTdoSHrN7xjR-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Last year, I fell in love with the <a href="https://www.tomsguide.com/cameras-photography/dji-rs-4-mini-review-the-hands-free-gimbal-youve-been-waiting-for">DJI RS 4 Mini </a>because of its balance of portability and features, including a newly introduced magnetic tracking module which allowed for hands-free recording. The downside? Its lower payload limited it to smaller camera and lens combos, and its tracker, as handy as it was for recording myself, could only recognize and follow humans. Enter the DJI RS 5.</p><p>In addition to its higher 6.6lbs payload which better supports more professional camera setups like my <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-tested-the-panasonic-lumix-s1r-ii-and-its-almost-the-perfect-hybrid-camera">Panasonic Lumix S1R II</a> as well as larger zoom lenses, the new RS 5 also takes the Mini's once exclusive tracking features and "supercharges" them with better recognition that can now follow vehicles and other inanimate objects. Those two upgrades alone have made the RS 5 a now crucial part of my video production.</p><p>Combine that with the RS 5's significantly improved balancing, which is the best I've ever tested, as well as faster charging which gets you from 0-100% in just an hour, and this is the ultimate gimbal for those with medium-to-large camera setups, especially for solo creators.</p><h2 class="article-body__section" id="section-dji-rs-5-review-specs"><span>DJI RS 5 review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Starting price</strong></p></td><td  ><p>Starts at <a href="https://a.co/d/0hle2zC4" target="_blank" rel="nofollow">$569</a> / <a href="https://www.amazon.co.uk/DJI-Stabilizer-Panasonic-Fine-Tuning-Quick-Open/dp/B0FLKBCT26/" target="_blank" rel="nofollow">£485</a></p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.3lbs (Gimbal only)</p></td></tr><tr><td class="firstcol " ><p><strong>Max payload</strong></p></td><td  ><p>6.6lbs</p></td></tr><tr><td class="firstcol " ><p><strong>Axis locks</strong></p></td><td  ><p>Automated (Gen 2)</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>1.8-inch OLED Touchscreen</p></td></tr><tr><td class="firstcol " ><p><strong>Vertical shooting</strong></p></td><td  ><p>Native (Gen 2 Vertical Mount)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery</strong></p></td><td  ><p>14 hours (fast charge in 1 hour)</p></td></tr><tr><td class="firstcol " ><p><strong>Tracking</strong></p></td><td  ><p>Enhanced Intelligent Tracking Module</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-dji-rs-5-review-price-availability"><span>DJI RS 5 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:8217px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Teer3oyKvCpghBeqVDqowC" name="DJI RS 5 against a blue background" alt="DJI RS 5 against a blue background" src="https://cdn.mos.cms.futurecdn.net/Teer3oyKvCpghBeqVDqowC.jpg" mos="" align="middle" fullscreen="" width="8217" height="4623" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">The £549 DJI Osmo Pocket 4 Creator Combo, with all components packed into the included carry case. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Paul Antill / Tom's Guide)</span></figcaption></figure><p>The DJI RS 5 is available in two main configurations. The standalone gimbal with the battery grip and quick open tripod foot for <a href="https://a.co/d/0hle2zC4" target="_blank" rel="nofollow">$549</a> / <a href="https://www.amazon.co.uk/DJI-Stabilizer-Panasonic-Fine-Tuning-Quick-Open/dp/B0FLKBCT26/" target="_blank" rel="nofollow">£485</a>, while the Combo, which I'm testing, adds the electronic briefcase handle and the Enhanced Intelligent Tracking Module for <a href="https://www.amazon.com/DJI-Stabilizer-Intelligent-Electronic-Fine-Tuning/dp/B0FLKB8TB5/" target="_blank" rel="nofollow">$749</a> /<a href="https://www.amazon.co.uk/DJI-Stabilizer-Intelligent-Electronic-Fine-Tuning/dp/B0FLKB8TB5/" target="_blank" rel="nofollow"> £619</a>. Optionally, you can also purchase the DJI RS Enhanced Intelligent Tracking Module separately for $99 if you don't need the handle or want to pick up the tracker down the road.</p><p>At the time of this review, an RS 5 Pro model is not available with no release date — if any — in sight. DJI tends to space out their releases which means for now at least if you need a gimbal that can support up to 10lbs,  you'll need to go with last year's RS 4 Pro.</p><p>While DJI continues to face U.S. government bans on its cameras, <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">drones</a>, and other new products, the RS 5 is available at most major retailers, although no first-party availability through DJI’s website means no aftercare plans. It'll be interesting to see how these tensions impact DJI's future releases but for now, their gimbal lineup doesn't seem to be as impacted. </p><h2 class="article-body__section" id="section-dji-rs-5-review-setup-balancing"><span>DJI RS 5 review: Setup & balancing</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1840px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="3TQbJ6vkfN6QNgsXjkEbZR" name="DJI RS 5 - With Lumix S1Rii & 18mm Lens" alt="DJI RS 5 with Lumix S1Rii & 18mm Lens" src="https://cdn.mos.cms.futurecdn.net/3TQbJ6vkfN6QNgsXjkEbZR.jpg" mos="" align="middle" fullscreen="" width="1840" height="1035" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Paul Antill / Tom's Guide)</span></figcaption></figure><p>While DJI’s gimbal design has remained largely unchanged over the last few years, each new model brings refinements to ergonomics, usually shaves off a bit of weight, and improves efficiency. The RS 5 is no exception as it looks and feels nearly identical to last year's DJI RS 4, including the same automated axis locks that let the gimbal automatically fold and unfold itself in one click. However, the RS 5 has a number of less obvious but much appreciated upgrades.</p><p>Balancing gimbals has always been my least favorite part. Back in my days as a wedding videographer, I would have to strategically find the best time to swap out lenses. Up until now, it was a constant slide-back-and-forth situation where you’d nudge the arm and try not to overshoot the balance point — which was especially difficult with heavier cameras attached.</p><p>While the RS 4 had a fine-tuning knob for its rail adjustment, the RS 5 takes things further by adding them to all three axes, using a leadscrew mechanism for millimeter-level precision with a few quick turns rather than the push-and-pull struggle from before. In my testing, this made a massive difference. Switching from a light and short 35mm prime lens to a much longer and heavier 24-60 zoom lens took about 10 seconds rather than 1-2 minutes. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Kgkq73uYN4KGv3Ka4eVSrQ.jpg" alt="DJI RS 5 Axis Knobs" /><figcaption><small role="credit">Paul Antill / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/yMBD6SuTwkwZmQCEo4zSnA.jpg" alt="DJI RS 5 Axis Knobs" /><figcaption><small role="credit">Paul Antill / Tom's Guide</small></figcaption></figure></figure><p>DJI also added a new on screen Z‑Axis Indicator that gives you instant feedback to your setup's balance as you move around to help you adjust your movements for steadier shots. Another small but still cool upgrade is found in its tripod foot which now opens all three feet at the same time instead of the usual one at a time. It's another thing that shaves off time and makes the RS 5 the fastest gimbal to setup that I've ever tested.</p><p>For heavier camera setups or low-angle shots, the electronic briefcase handle which comes with the RS 5 Combo can help distribute the weight between your two handles more easily. Unlike previous bundled handles, this one features a joystick and buttons that can control the gimbal and camera from a more convenient position. </p><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="2UjMuEWPh6thrABYWZq4sf" name="DJI RS 5 - Z Axis" alt="DJI RS 5 Z Axis balance on screen tool" src="https://cdn.mos.cms.futurecdn.net/2UjMuEWPh6thrABYWZq4sf.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>Just like its predecessor, the RS 5 is rated for 6.6lbs, though DJI claims it offers 50% more motor torque for added stability, especially when doing fast "follow" movements or low-angle shots which tend to put more stress on the gimbal. For most full size <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">mirrorless camera</a> combos, this continues to be the sweet spot. </p><p>But despite these upgrades, DJI has put itself in a very awkward spot right now. For anyone looking for a lighter gimbal to support their smaller APS-C or Micro Four Thirds cameras, they're probably best getting last year's DJI RS 4 Mini, and for higher payloads, they'll need to go with last year's RS 4 Pro. </p><h2 class="article-body__section" id="section-dji-rs-5-review-battery-life"><span>DJI RS 5 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="UaKDYKRbqqJuS5oDDt4Qdj" name="DJI RS 5 Front view" alt="DJI RS 5 Front view" src="https://cdn.mos.cms.futurecdn.net/UaKDYKRbqqJuS5oDDt4Qdj.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>The DJI RS 5 gets a slight boost to battery life, now rated for 14-hours. The battery grip is still removable if you wanted to swap in a fresh set. More impressive, though, are significantly faster charging speeds. Last year, the RS 4 took about two and a half hours to fully charge while the RS 5 cuts that down to just one hour. A quick 15-minute charge between shots could get me a few extra hours of run time. </p><h2 class="article-body__section" id="section-dji-rs-5-review-improved-tracking"><span>DJI RS 5 review: Improved tracking</span></h2><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="n5HSonsuSw8qBucFrbadNP" name="DJI RS 5 - Tracking Module" alt="DJI RS 5 Tracking Module" src="https://cdn.mos.cms.futurecdn.net/n5HSonsuSw8qBucFrbadNP.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>Saving my favorite feature for last, the new RS Enhanced Intelligent Tracking Module has transformed how I use the DJI RS 5. Previously I relied on the DJI RavenEye accessory, which required an HDMI feed and USB-C connection from the camera that was clunky and worked about half of the time (if I was lucky).</p><p>When DJI first introduced their revamped tracking camera module with the RS 4 Mini, which attached magnetically and communicated directly with the gimbal and displayed a live feed right on the touchscreen without any additional setup: I was impressed. But as I mentioned earlier, I was a bit underwhelmed that tracking was limited to human subjects only.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="dDWg4cFURCAwyxBENeuqpk" name="DJI RS 4 Mini - Tracking Module" alt="DJI RS 4 Mini Tracking Module" src="https://cdn.mos.cms.futurecdn.net/dDWg4cFURCAwyxBENeuqpk.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>Don't get me wrong: as a solo creator, the gesture controls are still a lifesaver. An open palm tells the gimbal to start tracking me, indicated by the tracker ring light turning green. When I want it to stop, I make another open palm and light turns red. When my camera is paired, a peace sign starts or stops recording and a "Double L" gesture even lets me pause tracking to reposition myself and then resume after I've moved. </p><p>Now, the RS 5 takes that foundation and makes it significantly more valuable by introducing non-human tracking as well. The tracker can now lock onto almost anything, whether it is a building in the distance, a car driving by, or a stationary product on my desk. This allows for much easier capture of smooth, cinematic movements because you can focus more on the gimbal movement itself and less on keeping your subject in frame. </p><p>One trick I really like to do is combo this with a camera slider, even a "dumb" one that just slides back and forth on a loop. On the gimbal touchscreen, I simply draw a box on the subject I want to track, usually another product I am reviewing, and the gimbal stays perfectly locked on as it moves along the rail. It creates high-end production value with minimal effort which is always what I'm trying to achieve at the end of the day. Whether you get it as part of the Creator Combo or as a separate $99 add-on, I think its a no-brainer accessory if you want to get the most out of the RS 5.</p><h2 class="article-body__section" id="section-dji-rs-5-review-verdict"><span>DJI RS 5 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="ZgjpyGa9TvmjiT4xp6pjUZ" name="DJI RS 5 - Side view" alt="DJI RS 5 in hand" src="https://cdn.mos.cms.futurecdn.net/ZgjpyGa9TvmjiT4xp6pjUZ.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: Paul Antill / Tom's Guide)</span></figcaption></figure><p>If you are looking for the ultimate gimbal for a medium sized camera setup, DJI raises the bar once again with the RS 5. By combining improved efficiency, speed and smart tracking features, DJI has created one of the best tools for solo camera operators. The improved balancing system alone could justify the upgrade for many. It'll be interesting to see if and when DJI releases an updated Mini and Pro gimbal, but right now, this is the most well-rounded gimbal you can get today. </p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ The Leica SL3-S is one of the most beautiful cameras I’ve ever used — here are 3 things I like (and 1 I don’t) ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/the-leica-sl3-s-is-one-of-the-most-beautiful-cameras-ive-ever-used-here-are-3-things-i-like-and-1-i-dont</link>
                                                                            <description>
                            <![CDATA[ I've spent the last two weeks using the Leica SL3-S to capture 24MP stills and 6K Open Gate video, and there are many things to love about the camera. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">7Uregfyec7tBeHJPPw7Jrf</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/p2akZWyRUuonp6zYMyv8eS-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sat, 25 Apr 2026 12:00:00 +0000</pubDate>                                                                                                                                <updated>Sat, 25 Apr 2026 12:50:51 +0000</updated>
                                                                                                                                            <category><![CDATA[Mirrorless Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/p2akZWyRUuonp6zYMyv8eS-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A black Leica SL3-S mirrorless camera]]></media:description>                                                            <media:text><![CDATA[A black Leica SL3-S mirrorless camera]]></media:text>
                                <media:title type="plain"><![CDATA[A black Leica SL3-S mirrorless camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/p2akZWyRUuonp6zYMyv8eS-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As a photographer, few things make me happier than using an outstanding camera that takes stunning photos. And as a journalist here at Tom's Guide, I review a lot of consumer tech, including the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a>. That means I get to play with the latest and greatest cameras, capturing stunning sights, and call it work. It's a fantastic job.</p><p>For the last two weeks, I've been testing one such camera, and that's the Leica SL3-S. The SL3-S is the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-sl3-review">Leica SL3</a>'s sibling, and both cameras share the same body, autofocus system, and L-mount. The SL3-S, however, is aimed at hybrid content creators who work with both stills and video, and it boasts faster burst shooting (up to 30fps) and 6K Open Gate video. <em>And </em>it's cheaper!</p><div class="product"><a data-dimension112="8e29f045-a810-4b98-b449-1b7a4a0689b3" data-action="Deal Block" data-label="The Leica SL3-S is a high-performance full-frame mirrorless camera designed for professional photography and video. Its 24MP BSI CMOS sensor, in conjunction with its Maestro IV processor, captures stunning photos as well as highly detailed 6K video. 30fps burst shooting makes the SL3-S faster than its counterpart, the SL3, too. Also, IP54 waterproofing and dustproofing make the SL3-S extremely rugged." data-dimension48="The Leica SL3-S is a high-performance full-frame mirrorless camera designed for professional photography and video. Its 24MP BSI CMOS sensor, in conjunction with its Maestro IV processor, captures stunning photos as well as highly detailed 6K video. 30fps burst shooting makes the SL3-S faster than its counterpart, the SL3, too. Also, IP54 waterproofing and dustproofing make the SL3-S extremely rugged." data-dimension25="$5665" href="https://www.bhphotovideo.com/c/product/1873126-REG/leica_10643_sl3_s_mirrorless_camera.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1116px;"><p class="vanilla-image-block" style="padding-top:76.16%;"><img id="AuQTMCqzHYdDrhvK5LU9cV" name="Leica-SL3S-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/AuQTMCqzHYdDrhvK5LU9cV.png" mos="" align="middle" fullscreen="" width="1116" height="850" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Leica SL3-S is a high-performance full-frame mirrorless camera designed for professional photography and video. Its 24MP BSI CMOS sensor, in conjunction with its Maestro IV processor, captures stunning photos as well as highly detailed 6K video. 30fps burst shooting makes the SL3-S faster than its counterpart, the SL3, too. Also, IP54 waterproofing and dustproofing make the SL3-S extremely rugged.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1873126-REG/leica_10643_sl3_s_mirrorless_camera.html" target="_blank" rel="nofollow" data-dimension112="8e29f045-a810-4b98-b449-1b7a4a0689b3" data-action="Deal Block" data-label="The Leica SL3-S is a high-performance full-frame mirrorless camera designed for professional photography and video. Its 24MP BSI CMOS sensor, in conjunction with its Maestro IV processor, captures stunning photos as well as highly detailed 6K video. 30fps burst shooting makes the SL3-S faster than its counterpart, the SL3, too. Also, IP54 waterproofing and dustproofing make the SL3-S extremely rugged." data-dimension48="The Leica SL3-S is a high-performance full-frame mirrorless camera designed for professional photography and video. Its 24MP BSI CMOS sensor, in conjunction with its Maestro IV processor, captures stunning photos as well as highly detailed 6K video. 30fps burst shooting makes the SL3-S faster than its counterpart, the SL3, too. Also, IP54 waterproofing and dustproofing make the SL3-S extremely rugged." data-dimension25="$5665">View Deal</a></p></div><p>The SL3-S is one of the most beautiful cameras I've had the pleasure of using or testing. It handles sublimely thanks to its stripped back controls and straightforward menu system. It takes gorgeous photos with its 24MP BSI CMOS sensor, and there's 30fps burst shooting too, for anyone partaking in high-speed photography. </p><p>There are plenty of things to love about the SL3-S but equally so, there's one thing I dislike and can't look past. Without further ado, here are three things I like about the SL3-S, and one I don't.</p><h2 class="article-body__section" id="section-what-i-like"><span>What I like</span></h2><h2 id="sublime-handling-and-controls">Sublime handling and 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:6956px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="wW89bYDNUjmJnPPHyKZ9xS" name="Leica-SL3S-product" alt="A black Leica SL3-S mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/wW89bYDNUjmJnPPHyKZ9xS.jpg" mos="" align="middle" fullscreen="" width="6956" height="3913" 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>One of the things I've always appreciated about Leica cameras is their build quality and handling. The Leica SL3-S is no different. It bears striking resemblance to the SL3: it's the same body and the same controls, and I have no complaints there. It measures 5.55 x 4.25 x 3.33 inches and the body weighs 1.69lbs (without battery, SD card and camera bayonet cover).</p><p>Though it's a little heavy with a lens attached, it isn't unwieldy to handle. The chunky grip on the right-hand side protrudes quite a bit, and it's perfect for my medium-sized hand to wrap around. The OLED EVF with its resolution of 5.76M dots and refresh rate of 60Hz/120Hz is bright and comfortable to look through too. In addition to that, I love the dual displays: a 3.2-inch rear monitor with a resolution of 2.33M dots, and a 1.28-inch LCD which quickly shows you the active settings.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5975px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="saHCBzFF4fFg4pcFFEP8DS" name="Leica-SL3S-product-9" alt="A black Leica SL3-S mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/saHCBzFF4fFg4pcFFEP8DS.jpg" mos="" align="middle" fullscreen="" width="5975" height="3361" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>I also appreciate how rugged and robust the SL3-S is. Its body is IP54-rated, which means it's protected from dust ingress and water spray from any direction. Having shot with the SL3-S in the rain (typical British weather for you), I can confirm that the SL3-S is indeed waterproof, making it a fantastic choice for landscape photographers or anyone who shoots outdoors, really.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:929px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="QZ2cqESitb3z8wtZMtDVcR" name="Leica-SL3S-product-8" alt="A black Leica SL3-S mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/QZ2cqESitb3z8wtZMtDVcR.jpg" mos="" align="middle" fullscreen="" width="929" height="523" 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>Keeping in line with Leica's luxurious philosophy, controls on the SL3-S are stripped back and well-laid out as well, as there aren't too many overwhelming buttons and dials. Similarly, the user interface and menu system is extremely clean and well-signposted. If you want a detailed breakdown of the controls and design, check out my <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-sl3-review#section-leica-sl3-review-design">Leica SL3 review</a> — like I said, both the SL3 and SL3-S are visually identical.</p><h2 id="sharp-image-quality">Sharp image quality</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/w2MjJaWPSiM8C6AVaaFrRH.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/kCtVxoiLBRZBpukCFbp27H.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/84ZQnCaPZc9W6FViUM7XLH.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hwCQrjnQmT2iC9vszE4j9K.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mUQUC3Vifeoy8f4dWdip7K.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YVPuxBjhwH5NpmZS8Aon9K.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/reCVAmdJs6AWSqScZ7CSFK.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/DUZWURXfrngBn4u5jRRdDK.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JPJDnpF4wz3bo88Ucx9KbJ.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Unlike the Leica SL3 which utilizes a 60.3MP sensor, the Leica SL3-S settles for a 24MP BSI CMOS sensor to prioritize speed (30fps burst up from 15fps) and improved video capabilities. Even so, the 24MP images the SL3-S captures are gorgeous, and they still pack plenty of detail. While they aren't suited for large format printing or extreme crops, they're ideal for online use, portfolios and making small prints.</p><p>I've always been a fan of Leica's colors, and you can see just how beautiful the different shades look in the galleries above (shot in the Standard color profile) and below. The purples and blues appear deep and rich, while the pinks pop, and the camera has captured the pastel pink shades well too. The images are very sharp, as you can see every seed head on the dandelion in the third slide, and the red flower's stigma in the fifth slide above.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ShvJLNpfWoVkZxW7UuZaXH.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/uaicLwzsNzJjXvUJAwmSjH.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/AvpRT3C3HUJ5hVMvWeW5KJ.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Just like on its other cameras, Leica offers users access to a few color profiles on the SL3-S: Standard, Vivid, Natural, Monochrome, and Monochrome High Contrast. The photos in the gallery above were shot with the Vivid color profile toggled. I love how deep the greens appear in the first and second slides, with just the right amount of depth to contrast against the bright pink flowers.</p><p>In terms of the camera's shooting settings, the electronic shutter has a shutter speed of 60s to 1/16000s, while the mechanical shutter has a speed of 30m to 1/8000s. The SL3-S has an ISO range of 50-200,000, up from 100,000 on the SL3. There's 5-axis in-body image stabilization (IBIS) at play too, providing up to 5.5 stops of compensation, enabling you to shoot at slow shutter speeds.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/koJe6qPiyPqJoUyRGTEv3K.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QJr273uPxH6pugnfdxP8DK.jpg" alt="A photo taken on a Leica SL3-S mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JZvfkSN59uQzQjr3zJsgfA.jpg" alt="A photo taken on a Leica SL3-S hybrid mirrorless camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Alongside this, the SL3-S boasts an accurate and fast hybrid autofocus system which uses 315 contrast AF and 779 phase detection AF points to quickly lock on to animals/birds and humans. As someone who loves photographing wildlife and taking portraits of my partner, I thoroughly appreciated the SL3-S' autofocus. In the gallery above, it was quick to focus on the pigeons outdoors, and my partner in low-light conditions indoors.</p><p>The photo of my partner was shot in the Monochrome High Contrast color profile which has remained a constant favorite of mine every time I've tested a Leica camera. The portrait exudes drama and the inky blacks look stunning. Overall, the SL3-S is a brilliant and capable snapper, and the image quality doesn't give you much to complain about.</p><h2 id="6k-video-is-simply-stunning">6K video is simply stunning</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5765px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5xyZGDkqvchP3XMPaE4bHS" name="Leica-SL3S-product-6" alt="A black Leica SL3-S mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/5xyZGDkqvchP3XMPaE4bHS.jpg" mos="" align="middle" fullscreen="" width="5765" height="3243" 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>While the Leica SL3-S is a capable stills shooter, it's a video-first camera, which is what distinguishes it from the SL3. The SL3-S is capable of recording 6K video at 30fps, 4K at 60fps, and FHD at 120fps. There's also internal 5.8K ProRes capture, as well as 10-bit color depth, which means the camera is capable of capturing over a billion individual hues and shades.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/gSGWkIzv_u4" allowfullscreen></iframe></div></div><p>One of the SL3-S' biggest selling points is that it records Open Gate video in 6K, which means the camera is recording the entire sensor area (3:2), rather than cropping to 16:9. This gives you more freedom to reframe in post-production. Theoretically, it enables users to create 4K videos in both horizontal and vertical formats, fit for social and long-form video.</p><p>Video footage captured on the SL3-S is stupendously good-looking. I captured the footage above in 6K Open Gate and reframed it to 16:9 in post. As you can see, the movement of the flowers is smooth and fluid, the colors are gorgeous, and the clips have lovely background bokeh which looks pleasing to the eye. I relied on the monitor to shoot these clips, and I was able to see the screen even in direct sunlight.</p><p>Audio quality isn't too shabby either, with the sounds of birds tweeting and the breeze brushing against the leaves sounding clean. Wind buffeting is a slight problem so if you're serious about video work, I'd recommend hooking up one of the <a href="https://www.tomsguide.com/best-picks/best-microphones">best microphones</a> via the 3.5mm audio input jack.</p><h2 class="article-body__section" id="section-what-i-don-t-like"><span>What I don't like</span></h2><h2 id="battery-life-remains-leica-s-biggest-hurdle">Battery life remains Leica's biggest hurdle</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:7045px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="jiPtwS7rZZnn5Mu4ZQvryS" name="Leica-SL3S-product-3" alt="A black Leica SL3-S mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/jiPtwS7rZZnn5Mu4ZQvryS.jpg" mos="" align="middle" fullscreen="" width="7045" height="3963" 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 Leica SL3-S, the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-q3-43-review">Leica Q3 43</a>, the <a href="https://www.tomsguide.com/cameras-photography/leica-m-ev1-review">Leica M EV1,</a> and the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-sl3-review">Leica SL3</a> all have one thing in common — and it ain't the high price points. I've tested my fair share of Leica cameras, and one things that's been a common denominator amongst most of the models is the short battery life. If we're going based on CIPA ratings alone (done under strict testing conditions), Leica lags behind its rivals from Fujifilm, Sony and Canon.</p><p>For instance, the Q3 43 is CIPA-rated for 350 shots; the M EV1 for 244 shots; the SL3 for 260 shots; and the SL3-S for 315 shots. That's... not a lot. But like I said, CIPA battery tests are conducted under very strict conditions, so you'll probably get more shots in real-world use anyway, depending on the camera's settings. In my testing, I was able to achieve approximately 330 shots between charges — and I was shooting in JPEG and DNG, and utilizing both the viewfinder and the monitor for framing/previewing images.</p><p>Even though that's 15 shots more than the CIPA-rated battery life, it's still not great, and if you throw video into the mix, it might get even worse. That's why it's recommended to carry an additional BP-SCL6 (<a href="https://www.bhphotovideo.com/c/product/1768279-REG/leica_19531_battery_bp_scl6_black.html" target="_blank" rel="nofollow">$240</a>) battery pack. But that really shouldn't be a requirement, in my opinion, when you're already spending nearly $6,000 on a camera. If and when Leica decides to introduce a successor to the SL3 and the SL3-S, I hope there's a big bump to battery life. I'll stop complaining then.</p><h2 class="article-body__section" id="section-looking-ahead"><span>Looking ahead...</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6130px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="TKBcnXJQwfNgS9gxZNQmKS" name="Leica-SL3S-product-5" alt="A black Leica SL3-S mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/TKBcnXJQwfNgS9gxZNQmKS.jpg" mos="" align="middle" fullscreen="" width="6130" height="3448" 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>Although mediocre battery life remains a sticking point for the Leica SL3-S, it's something I'm willing to overlook because of everything else the camera offers. Just like the Leica SL3, its hybrid counterpart is a joy to handle. Buttons and dials are sensibly laid out and they never feel overwhelming. The stripped back menu is a pleasure to navigate as all the settings are easy to find.</p><p>But it's the SL3-S' video and photo capabilities that are its biggest sell. 6K Open Gate video means you're recording using the entire camera sensor area at 6K resolution, rather than cropping to 16:9. The video quality is drool-worthy, as are the 24MP images the SL3-S captures.</p><p>I've thoroughly enjoyed my time with the SL3-S, and though I'd personally prefer the SL3's 60.3MP sensor as a stills-first photographer, there really isn't much else I can complain about. And if you don't mind dropping to a lower-res sensor, the SL3-S comes in cheaper than the SL3 too, with the body retailing for <a href="https://www.bhphotovideo.com/c/product/1873126-REG/leica_10643_sl3_s_mirrorless_camera.html" target="_blank" rel="nofollow">$5,665 at B&H Photo</a> / <a href="https://www.cliftoncameras.co.uk/leica-sl3-s-body-only" target="_blank" rel="nofollow">£4,489 at Clifton Cameras</a>. If I win the lottery, I won't tell anyone, but there will be signs.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-O6P5Qe"></div>                            </div>                            <script src="https://kwizly.com/embed/O6P5Qe.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-sl3-review">I’ve been testing the Leica SL3 — here’s my honest verdict on whether it’s worth $7,500</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/ive-been-testing-the-leica-sl3-its-the-only-camera-thats-made-me-want-to-ditch-my-fujifilm">I’ve been testing the Leica SL3 — it’s the only camera that’s made me want to ditch my Fujifilm</a></li><li><a href="https://www.tomsguide.com/cameras-photography/i-tested-and-loved-the-controversial-leica-m-ev1-but-id-choose-its-4-5-star-camera-instead">I tested and loved the controversial Leica M EV1, but I’d choose this 4.5-star camera instead</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Blurb Photo Book review ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/blurb-photo-book-review</link>
                                                                            <description>
                            <![CDATA[ The Blurb photo book service is hard to recommend because of print imperfections and clunky software. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">RQAqDu4xpqTfKQGYSVqrfe</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/q5EqokkPMJNAg5uGXxk7L6-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 24 Apr 2026 16:15:45 +0000</pubDate>                                                                                                                                <updated>Tue, 09 Jun 2026 10:32:32 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras &amp; Photography]]></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/q5EqokkPMJNAg5uGXxk7L6-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Blurb photo book photographed on a table in front of a blue background]]></media:description>                                                            <media:text><![CDATA[Blurb photo book photographed on a table in front of a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[Blurb photo book photographed on a table in front of a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/q5EqokkPMJNAg5uGXxk7L6-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I’ve tested my fair share of the <a href="https://www.tomsguide.com/best-picks/best-photo-books">best photo books</a> available, so I can say with complete certainty that the Blurb photo book isn’t one of them. After slaving away to create a photo book, you want a result that you’re happy and proud to share with friends and family when they come to visit. You may even want to get a book as a gift for someone. But the Blurb isn’t one I’d be happy to gift someone. </p><p>Color replication is dull and lackluster, while black and white images suffer from overly dark blacks,  losing all shadow detail and dynamic range. After only flipping through the book a couple of times, the front cover started to crack, which was a letdown. And there was an issue with ink transfer across the pages inside. </p><p>But if you want to see what I didn’t like about this book and what you should try instead, keep reading this full Blurb photo book review.</p><h2 class="article-body__section" id="section-blurb-photo-book-review-specs"><span>Blurb Photo Book review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.blurb.com/photo-books" target="_blank" rel="nofollow">From $12</a></p></td></tr><tr><td class="firstcol " ><p><strong>Cover options</strong></p></td><td  ><p>ImageWrap Hardcover, Softcover, Linen Hardcover with Dust Jacket</p></td></tr><tr><td class="firstcol " ><p><strong>Cover finish options</strong></p></td><td  ><p>Matte, glossy</p></td></tr><tr><td class="firstcol " ><p><strong>Paper options</strong></p></td><td  ><p>Standard, Premium Lustre, Premium Matte, Mohawk proPhoto Pearl, Mohawk Superfine Eggshell</p></td></tr><tr><td class="firstcol " ><p><strong>Binding style</strong></p></td><td  ><p>Layflat</p></td></tr><tr><td class="firstcol " ><p><strong>Number of pages</strong></p></td><td  ><p>20-440 (standard), 20-240 (all other paper options)</p></td></tr><tr><td class="firstcol " ><p><strong>Sizes</strong></p></td><td  ><p>7x7 / 12x12 / 8x10 / 10x8 / 13x11 inches</p></td></tr><tr><td class="firstcol " ><p><strong>App</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Standard delivery time</strong></p></td><td  ><p>7 to 11 business days</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-blurb-photo-book-review-price-availability"><span>Blurb Photo Book review: Price & availability </span></h2><p>The Blurb photo book is available to purchase starting <a href="https://www.blurb.com/photo-books" target="_blank" rel="nofollow">from $12 at the Blurb website</a>. The $12 price is for the cheapest option, which is the softcover, but I tested out the hardcover option, which is a lot more sturdy. This option starts <a href="https://www.blurb.com/photo-books/imagewrap-hardcover-photo-book" target="_blank" rel="nofollow">from $32 on the Blurb website</a>. I opted for this as the hardcover <em>should</em> last a lot longer (more on this later), which is what you want when preserving memories. </p><p>I created a 12 x 12-inch ImageWrap Hardcover photo book with 20 pages and the matte standard paper. This cost $83, and I also got a PDF of the photo book. You can make further upgrades to the photo book, like more premium paper options for additional cost. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1820px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Jswwfj4ZsLbirLZfoGeuC6" name="Blurb" alt="Blurb photo book photographed on a table in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/Jswwfj4ZsLbirLZfoGeuC6.jpg" mos="" align="middle" fullscreen="" width="1820" height="1024" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>While Blurb was easy to create, I much prefer the results I achieved with <a href="https://www.tomsguide.com/cameras-photography/popsa-photo-book-review">Popsa</a>, which was only three dollars more at $86 for a similar style book. Blurb offers a convenient photo book creation process, but is let down majorly in the quality department. The Popsa results are so much better for the same price, so it’s hard to justify spending on Blurb. </p><p>If you’re not fussed about having the premium feel of Popsa and don’t want to spend so much, <a href="https://www.tomsguide.com/cameras-photography/chatbooks-photo-book-review">Chatbooks</a> offers a lot of customization, and I got a 10x10-inch book for $50. Alternatively, if you want something more premium-looking to display on your coffee table, you can look at the <a href="https://www.tomsguide.com/reviews/printique">Printique</a> photo book service, which would cost around $110 for a similar-sized book that has excellent color and contrast replication.</p><h2 class="article-body__section" id="section-blurb-photo-book-review-software-production"><span>Blurb Photo Book review: Software & production</span></h2><p>Creating the Blurb photo book felt quite limited when designing. After testing out services like Popsa and Mixbook, which both have easy-to-navigate interfaces, Blurb felt pretty clunky. Sure,  I got the hang of it after much trail and error, and then it was fairly easy to use, but it was tricky at first. The service uses BookWright Online, which is a book design software that can be used in your desktop browser, so there’s no need to download any software. </p><p>Unfortunately, this software isn’t compatible with mobile devices, so you need to use a computer for it to work. It took a while for me to get to grips with all of the tools on the site, and the layout options are pretty limited, which made me feel like I couldn’t be very creative. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1820px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="xTKbXeYQwXgqDfzXYvyys5" name="Blurb" alt="Blurb photo book photographed on a table in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/xTKbXeYQwXgqDfzXYvyys5.jpg" mos="" align="middle" fullscreen="" width="1820" height="1024" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I first uploaded the images I wanted to use to the site and could drag images from the bank to where I wanted them in the book. However, I had to have a frame for the image set out first, or I couldn’t place the image. I found it best to choose a frame from the layout section and customize the existing layout styles, but this was quite time-consuming. </p><p>Options for decoration are also limited. You can change the background color and add some text, but there aren’t any decorative elements. This obviously depends on the style of book you’re going for, but I enjoy adding a little whimsy to my books. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1820px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="2Hk9wyMPS9q79PznhAgwh6" name="Blurb" alt="Blurb photo book photographed on a table in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/2Hk9wyMPS9q79PznhAgwh6.jpg" mos="" align="middle" fullscreen="" width="1820" height="1024" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>For the easiest experience, especially for beginners, I would recommend using Popsa, but if you’re someone who wants to add your own creative flair to the book, then <a href="https://www.tomsguide.com/cameras-photography/mixbook-photo-book-review">Mixbook</a> is certainly the way to go.</p><h2 class="article-body__section" id="section-blurb-photo-book-review-print-quality"><span>Blurb Photo Book review: Print quality</span></h2><p>Having tested a bunch of other photo book services, I was very disappointed with the results from Blurb. I’m someone who loves to see the vibrancy of the colors in my photos and the pages, and I didn’t get that result here — not even close. Everything felt very washed out, making the pages feel flat rather than full of life. </p><p>The book also struggles with dynamic range and contrast in black and white images. The blacks look too black, meaning that any shadow detail in the darker portions of the image is completely lost. However, the highlights are well presented and not blown out. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1820px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="U4PChbKsAShy3Bvwy67jD6" name="Blurb" alt="Blurb photo book photographed on a table in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/U4PChbKsAShy3Bvwy67jD6.jpg" mos="" align="middle" fullscreen="" width="1820" height="1024" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>After flipping through the book a couple of times, I noticed that the cover on the front started to crack. I wasn’t putting any extra strain on the pages, but where the book was folding was cracking from repeatedly opening and closing the book. This is especially disappointing given I paid for the more expensive hardback precisely for longevity.</p><p>I also noticed an issue with some color from the pages transferring over to the opposite pages. I only picked up on this because, on my first look through the book, some of the pages were stuck together from where the book had been closed without fully drying. This meant ink was removed from some pages and transferred to the other side, making the whole thing look a little messy and, honestly, cheap. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1820px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="bPJkSzzwayZquSRvCXkMa6" name="Blurb" alt="Blurb photo book photographed on a table in front of a blue background" src="https://cdn.mos.cms.futurecdn.net/bPJkSzzwayZquSRvCXkMa6.jpg" mos="" align="middle" fullscreen="" width="1820" height="1024" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>There are also visible pixels on all of the images. I haven’t experienced this with any other photo book when using the exact same images at the same size. Overall, while the photobook looks nice at a glance, closer inspection shows countless flaws that make it hard to recommend this service.</p><h2 class="article-body__section" id="section-blurb-photo-book-review-verdict"><span>Blurb Photo Book review: Verdict</span></h2><p>The Blurb photo book service is hard to recommend. While it’s easy to create the book once you get the hang of the software and there are some cheaper options to choose from, it’s just not worth the money spent.</p><p>Firstly, the design software is desktop-only, and I find it so much easier to use mobile software from other photo book services like Popsa. And the book itself feels cheaply made. After only flipping through a handful of times, the hardcover began to crack. On the inside of the book, there were also issues with ink transfer, poor color replication and contrast issues in black and white images. </p><p>If you hadn’t already guessed, I really don’t recommend Blurb.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested DJI’s new Osmo Pocket 4 — this is the only tool I'll recommend to content creators from now on ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review</link>
                                                                            <description>
                            <![CDATA[ The DJI Osmo Pocket 4 is the best all-in-one package for content creators, and there isn’t even any competition. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">x2kzo6tALv2w5iWHxbLAw5</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/RLwbb2rXviBr8q8jaFhrSQ-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 21 Apr 2026 14:49:49 +0000</pubDate>                                                                                                                                <updated>Tue, 09 Jun 2026 10:32:30 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras &amp; Photography]]></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/RLwbb2rXviBr8q8jaFhrSQ-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[DJI Osmo Pocket 4 against a blue background]]></media:description>                                                            <media:text><![CDATA[DJI Osmo Pocket 4 against a blue background]]></media:text>
                                <media:title type="plain"><![CDATA[DJI Osmo Pocket 4 against a blue background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/RLwbb2rXviBr8q8jaFhrSQ-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you’re in any doubt as to the DJI Osmo Pocket 4’s significance to today’s content creator market, let me clear things up with an anecdote of sorts. I go to a lot of press events for cameras, and by far the most common camera I see being used by my fellow mediafolk is the <a href="https://www.tomsguide.com/reviews/dji-osmo-pocket-3">DJI Osmo Pocket 3</a>.</p><p>YouTubers, vloggers, social media creators, journalists — all rely on the Pocket 3. Why? Because it’s absolutely fantastic. It wraps up everything you need for video content creation into a slick, high-performing package. So… why did DJI need to make the Osmo Pocket 4? It didn’t, to be honest. But it went ahead and did so anyway. </p><p>I’ll get this out of the way early. The Osmo Pocket 4 (OP4) is very much an evolution, not a revolution, to the chagrin of many interested parties, I’m sure. Before its launch, there were spy shots showing multiple lenses — people believed them precisely because the Osmo Pocket 3 (OP3) was so good at its job, the thought being that any new model would have to be radical to justify its own existence.</p><p>Alas, it isn’t radical, but there are a number of key upgrades under the hood. An improved 1-inch sensor and image processing, 4K/240fps slow motion, and film simulation profiles for in-camera grades, to name a few. So is this the new <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best camera</a> for vloggers? Find out in my full DJI Osmo Pocket 4 review.</p><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-specs"><span>DJI Osmo Pocket 4 review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Starting price</strong></p></td><td  ><p><a href="https://www.amazon.co.uk/DJI-Essential-Stabilization-Lossless-14%E2%80%91Stop/dp/B0FYQ1LFFK" target="_blank" rel="nofollow">£429</a> / U.S. launch pending</p></td></tr><tr><td class="firstcol " ><p><strong>Max resolution (standard)</strong></p></td><td  ><p>4K/60fps</p></td></tr><tr><td class="firstcol " ><p><strong>Max resolution (slow mo)</strong></p></td><td  ><p>4K/240fps</p></td></tr><tr><td class="firstcol " ><p><strong>Sensor size</strong></p></td><td  ><p>1 inch</p></td></tr><tr><td class="firstcol " ><p><strong>Rear screen</strong></p></td><td  ><p>2-inch touchscreen</p></td></tr><tr><td class="firstcol " ><p><strong>Internal memory</strong></p></td><td  ><p>107GB</p></td></tr><tr><td class="firstcol " ><p><strong>MicroSD slot</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Connectivity</strong></p></td><td  ><p>Bluetooth, Wi-Fi, USB-C</p></td></tr><tr><td class="firstcol " ><p><strong>Water resistance</strong></p></td><td  ><p>None</p></td></tr><tr><td class="firstcol " ><p><strong>Battery</strong></p></td><td  ><p>1,545 mAh</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>5.6 x 1.7 x 1.3 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>6.7 ounces</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-price-availability"><span>DJI Osmo Pocket 4 review: Price & availability</span></h2><p>Unfortunately, it doesn’t look like the Osmo Pocket 4 will be coming to the U.S. just yet. DJI has had a rocky time with the U.S. government in recent years, facing a ban on its <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">drones</a>, and having been slapped with hefty tariffs on its other imports. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="ryJg3h6sdompxvK9Ehh4wQ" name="DJI_osmo_pocket4 17.JPG" alt="DJI Osmo Pocket 4 against a blue background" src="https://cdn.mos.cms.futurecdn.net/ryJg3h6sdompxvK9Ehh4wQ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">The £549 DJI Osmo Pocket 4 Creator Combo, with all components packed into the included carry case. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>For many camera products, like the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-6-review">DJI Osmo Action 6</a> ($369) and <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-360-review">DJI Osmo 360</a> ($549), this has meant no “official” support — so no first-party availability through DJI’s website and no aftercare plans. But you can still buy those products (at a mark up) from third party suppliers like Amazon. Alas, the Pocket 4 doesn’t fit that trend, and DJI has confirmed it won’t be coming to the U.S. at all… yet!</p><p>From DJI:<em> “We do not have U.S. pricing. The Osmo Pocket 4 will not be available in the U.S. market as the application for authorization is still pending.”</em> If you’re American, you’ll need to buy the +2-year-old DJI Osmo Pocket 3 instead for now, which remains steeply priced in the U.S. at <a href="https://www.amazon.com/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD" target="_blank" rel="nofollow">$499</a> thanks to those tariffs.</p><p>The Osmo Pocket 4 is available everywhere else though, and in the U.K. it starts at <a href="https://www.amazon.co.uk/DJI-Essential-Stabilization-Lossless-14%E2%80%91Stop/dp/B0FYQ1LFFK" target="_blank" rel="nofollow">£429 from Amazon</a>. That’s actually a drop in launch price from the Pocket 3, which launched at £489, although you can pick up a Pocket 3 at the time of writing for <a href="https://www.amazon.co.uk/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD?th=1" target="_blank" rel="nofollow">£385</a>. </p><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-design-controls"><span>DJI Osmo Pocket 4 review: Design & controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:944px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RLwbb2rXviBr8q8jaFhrSQ" name="DJI_osmo_pocket4 1.JPG" alt="DJI Osmo Pocket 4 against a blue background" src="https://cdn.mos.cms.futurecdn.net/v2/t:549,l:513,cw:944,ch:531,q:80/RLwbb2rXviBr8q8jaFhrSQ.jpg" mos="" align="middle" fullscreen="" width="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 DJI Osmo Pocket 4 looks identical to its predecessor, with similar general proportions. And if it weren’t for some slightly different ridge patterning on either model, you’d struggle to tell the two apart. That’s no bad thing, though — it means the Pocket 4 is just as lightweight, pocketable and easy to use as its older sibling. Of course, build quality is typical DJI: second to none.</p><p>The controls are again very similar, with a gimbal joystick and record button at the front, plus a swivelling display that reveals two more buttons when in its horizontal position. These buttons are new — one is a dedicated quick zoom button and the other is a custom button for mapping your three most commonly-used functions via single, double and triple presses. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/5v8yA4inydLqVnzRPrnU2R.jpg" alt="DJI Osmo Pocket 4 against a blue background" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/t9wpkDiyWq6pyWKCU85QxQ.jpg" alt="DJI Osmo Pocket 4 against a blue background" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/cadiBK3ZthWUHnGGAJFrqQ.jpg" alt="DJI Osmo Pocket 4 against a blue background" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/AMnqCvxHy4kXLqHi9if6cQ.jpg" alt="DJI Osmo Pocket 4 against a blue background" /><figcaption><small role="credit">Future</small></figcaption></figure></figure><p>The custom button is super handy in theory, although the assignable options are quite limited. I regularly need to switch between gimbal modes, which is a bit fiddly in real time — being able to assign this to the custom button would’ve been ideal, but no such luck (not even via the C1/C2 custom modes, as these don’t save gimbal modes).</p><p>Most settings control is done via the touchscreen though, and it’s gorgeous. It’s bright, detailed and spacious enough for in-depth control of DJI’s intuitive and feature-packed menu system. I’m very familiar with DJI’s menu and find it easy to use in the heat of the moment, but if you aren’t, the learning curve isn’t too steep.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/UoZE5xsbqgP2XRSM5j6htQ.jpg" alt="DJI Osmo Pocket 4 against a blue background" /><figcaption><small role="credit">Future</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Wk8b8NSM8ZRT8Y6tKsZ2aQ.jpg" alt="DJI Osmo Pocket 4 against a blue background" /><figcaption><small role="credit">Future</small></figcaption></figure></figure><p>On the back of the gimbal is an electronic connector, allowing you to hook up an accessory — namely the clip-on vlogging fill light (available with the <a href="https://www.amazon.co.uk/DJI-Creator-Stabilization-Lossless-Transmitter/dp/B0FKT9K6CB" target="_blank" rel="nofollow">£549 Creator Combo</a>).</p><p>The Pocket 4 is not an action camera, so it isn’t waterproof. But that’s to be expected.</p><h4 id="connectivity-2">Connectivity</h4><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="uu8BMrBcWzjv4pfdrn3jsQ" name="DJI_osmo_pocket4 14.JPG" alt="DJI Osmo Pocket 4 against a blue background" src="https://cdn.mos.cms.futurecdn.net/uu8BMrBcWzjv4pfdrn3jsQ.jpg" mos="" align="middle" fullscreen="" width="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 Osmo Pocket 4 boasts the same wealth of connectivity options as its predecessor. It has a USB-C slot which enables the addition of an additional battery pack (very useful, pictured above). You can hook up wirelessly to your smartphone, and to Bluetooth accessories — and of course, connecting to DJI microphones via OsmoAudio (Bluetooth) is a mere screen-tap away. </p><p>Making a return is the electronic accessory connector on the back of the gimbal, too, for hooking up a vlogging light. In short, there’s everything you need for high-tier content creation on the go.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="BDMo6DiYmEiD28U3FJjx2R" name="DJI_osmo_pocket4 7.JPG" alt="DJI Osmo Pocket 4 against a blue background" src="https://cdn.mos.cms.futurecdn.net/BDMo6DiYmEiD28U3FJjx2R.jpg" mos="" align="middle" fullscreen="" width="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>There’s 107GB internal storage, up from nothing at all in the Pocket 3. This is the most generous internal storage DJI has yet fitted to one of its handheld cameras, and is more than double the 50GB found in its flagship action camera, the Osmo Action 6. DJI is championing internal storage these days, and I love it. It’s so reassuring to know you have extra space if your card fills or, worst case scenario, corrupts — you can still get the footage you need.</p><p>Naturally, there’s a MicroSD slot for expanding storage if needed, but with 107GB of storage on offer, I haven’t needed to yet. And besides, with internal storage, transfer speeds are faster (up to 800Mb/s).</p><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-stabilization"><span>DJI Osmo Pocket 4 review: Stabilization</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="VhoLT3ri3MmwhyutwbUBuQ" name="DJI_osmo_pocket4 8.JPG" alt="DJI Osmo Pocket 4 against a blue background" src="https://cdn.mos.cms.futurecdn.net/VhoLT3ri3MmwhyutwbUBuQ.jpg" mos="" align="middle" fullscreen="" width="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 DJI Osmo Pocket 4 uses the same 3-axis gimbal design as its predecessor, although there have been some internal tweaks to the algorithms, vastly improving stabilization, according to DJI. Honestly, the Pocket 3’s stabilization was excellent to begin with, so I’ve struggled to tell the difference. But hey, they’re both absolutely fantastic.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/iBACJi6B_uo" allowfullscreen></iframe></div></div><p>As you can see in the footage throughout this review, it’s super easy to shoot long, smooth pans and tilts. The stabilization also works wonders to iron out the bumpiness of walking. If you’re going to be vlogging on the move, especially when walking, there’s no better and more compact way to get ultra-smooth, professional footage than the Pocket 4.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/hC0lz40rmb0" allowfullscreen></iframe></div></div><p>There are three different gimbal modes to allow you to customize the behavior of the stabilization. I mainly stick to the default 'Follow' and 'Tilt Locked' (horizon locked) as these are great for everyday use, mostly minimizing drift but allowing some natural movement. There’s also an 'FPV-' mode, though, where the gimbal always matches the direction the screen is facing, until you use the joystick. It’s kinda like a manual mode, and I found it very useful for restricting the gimbal movement when I didn’t want drift or tracking.</p><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-autofocus"><span>DJI Osmo Pocket 4 review: Autofocus</span></h2><p>The DJI Osmo Pocket 4 brings back a couple of handy autofocus modes from the Pocket 3. There’s Spotlight Follow, which prioritizes registered subjects in frame. You add subjects and the camera tracks them over other stuff the Dynamic Active Tracking (i.e. automatic subject detection) detects. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3328px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5FhcAArTfUdNxw4Q2KHeZK" name="IMG_0865" alt="The DJI Osmo Pocket 4 with the screen being used to view tracked subjects in frame" src="https://cdn.mos.cms.futurecdn.net/5FhcAArTfUdNxw4Q2KHeZK.jpg" mos="" align="middle" fullscreen="" width="3328" height="1872" 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>Spotlight Follow sticks to the first-detected subject in any scene until that subject leaves the frame. It’s ideal for solo vlogging, although if you have multiple people in frame at different distances from the lens, you might end up with one out of focus as the camera prioritizes the first subject in frame.</p><p>There’s also a Dynamic Framing frame which allows you to set the focus area from 9 central points. It’s a helpful bit of manual control for when you need to lock focus.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3415px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="AmBeb3FnqrCSUzHnJPxwaK" name="IMG_0877" alt="The DJI Osmo Pocket 4 with the screen being used to view tracked subjects in frame" src="https://cdn.mos.cms.futurecdn.net/AmBeb3FnqrCSUzHnJPxwaK.jpg" mos="" align="middle" fullscreen="" width="3415" height="1921" 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>Honestly, though, I just left the camera out of these modes most of the time and in its basic autofocus and Dynamic Active Tracking (automatic subject detection and tracking) mode, which works really well. It prioritizes faces, which is what you’d want, but can recognize wider shots, prioritizing deeper focal range — switch to closer subjects if faces come into frame. It’s perfect for vlogging, though, and I never recorded a clip where my face was out of focus when talking to camera.</p><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-video-quality"><span>DJI Osmo Pocket 4 review: Video 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="QD9u6T39KMHHMQDnk4jixQ" name="DJI_osmo_pocket4 2.JPG" alt="DJI Osmo Pocket 4 against a blue background" src="https://cdn.mos.cms.futurecdn.net/QD9u6T39KMHHMQDnk4jixQ.jpg" mos="" align="middle" fullscreen="" width="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 OP4’s 4K footage looks beautiful and sharp, with bold, vibrant colors… although it’s not like the older OP3’s video looked bad. The lens gives a nice, wide field of view that doesn’t suffer from too much ugly barrel distortion, making it perfect for vlogging. There are two zoom levels, 2x and 4x, which are “lossless,” according to DJI — although I’m not really sure what that means, as you can see the resolution taking a big hit at the increased zoom levels in the vlogging clip below.</p><p>In normal video mode, the Pocket 4 shoots 4K at up to 60fps in 16x9 landscape orientation, and 3K at up to 60fps in 9x16 vertical orientation — the same as the Pocket 3. However, slow motion has received a big boost, with the OP3 able to shoot up to 4K/240fps (versus 4K/120fps), for twice as slow footage. Check out the vlogging video in this section to see an example.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/wBTZhStFd1o" allowfullscreen></iframe></div></div><p>Perhaps the main change to the core imaging gear is a revamped 1-inch sensor, which delivers super clean images with lots of detail, vibrant color, and increased dynamic range (up to 14 stops from 12 on the OP3). I was impressed with the dynamic range: standing in a shaded area on a bright day, the camera rendered both detailed shadows and bright highlights in the same frame.</p><p>There’s 10-bit color for over a billion individual tones, delivering more natural colors and smoother color transitions. 10-bit also provides more editing flexibility thanks to the greater amount of tonal data recorded. </p><p>DJI has done away with D-Log M, its historic “log” profile, which technically wasn’t a log profile but just a desaturated color profile. Now there’s D-Log Pro, which is legitimately a logarithmic gamma correction curve, and delivers wider dynamic range capture (the trade-off being it needs grading in post). DJI has also binned HLG HDR, which makes sense — this is primarily used by the broadcast media industry, so it always felt a bit redundant on vlogging cameras, like the Osmo Pocket line.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/09Xhi4dVIIA" allowfullscreen></iframe></div></div><p>If you want to apply some in-camera grades, the Pocket 4 has color filters, akin to the color filters on DJI’s action cameras. These are nothing special — no Fuji-level film-sim color science — but nice to have for some quick creative looks. I love DJI’s default color profile, which produces vibrant blues and greens on sunny days, so I mostly left the camera in that aside from testing.</p><p>There are also some new beauty filters, to smooth out skin tones and generally pep up appearance on vlogging. I’m not personally a fan of this type of thing, but if you are, they’re there for you to use.</p><h4 id="low-light-2">Low light</h4><p>The new 1-inch sensor, with its increased dynamic range, delivers cleaner shadows than the OP3, making the OP4 a better tool for low light footage. In moderately low light, the Low Light mode (capped at 4K/30fps) does a great job at brightening footage, and noise was barely perceptible (see the walking clip in the Stabilization section, which was shot in the Low Light mode).</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/Rva640E264M" allowfullscreen></iframe></div></div><p>In very dark conditions, as in the video above, the Pocket 4’s image sharpness takes a noticeable hit, with a watercolor effect on the background from the smoothing of in-camera noise reduction. However, this is a very challenging scenario, and I think the footage and smoothing looks pretty good, all things considered.</p><p>My Pocket 4’s auto exposure had a little bit of trouble in certain low light conditions, flickering constantly as it tried to balance exposure. This happened mostly in medium-dark situations, where there was a little bit of light entering the frame. It was solved by locking the exposure. I've reached out to DJI to establish why this might be, and if there's a fix in the works.</p><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-audio-quality"><span>DJI Osmo Pocket 4 review: Audio 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="pFhj7BMEcpnxQsRsNEnw2R" name="DJI_osmo_pocket4 15.JPG" alt="DJI Osmo Pocket 4 against a blue background" src="https://cdn.mos.cms.futurecdn.net/pFhj7BMEcpnxQsRsNEnw2R.jpg" mos="" align="middle" fullscreen="" width="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’ll be honest here and say that if you’re not looking to use the DJI Osmo Pocket 4 with a DJI microphone, you won’t be using it properly. This camera is designed to be used with the <a href="https://www.tomsguide.com/reviews/dji-mic-2"><u>DJI Mic 2</u></a>, Mic 3 or <a href="https://www.tomsguide.com/audio/dji-mic-mini-review"><u>Mic Mini</u></a>, and doing so will give you excellent, YouTube-quality audio, with clean vocals and strong ambient noise reduction — as you can hear in the walking vlog above.</p><p>Using OsmoAudio — i.e. pairing the camera via Bluetooth with one of DJI’s Mics — gives you access to four audio channel recording, spatial audio and audio zoom, where the audio direction matches the zoom area.</p><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="low" data-lazy-src="https://www.youtube-nocookie.com/embed/sp27OD2WuUI" allowfullscreen></iframe></div></div><p>That said, if you can’t stretch to a standalone mic just yet, or one of the creator combos that bundles a mic, the OP4 will still do the trick. Like the OP3, there are 3 internal microphones, plus wind reduction and voice priority modes. You can hear in the clip above how the internal mics sound in quiet and noisy conditions, with both those settings on. The result? Fine, and passable, but nothing like the quality you’ll get when hooking up a DJI mic.</p><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-app"><span>DJI Osmo Pocket 4 review: App</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5333px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="u8xR3sFT9qiEFs6ENRQYQn" name="DJI Mimo — Osmo Pocket 4" alt="Two screenshots of the DJI Mimo app in use with the Osmo Pocket 4" src="https://cdn.mos.cms.futurecdn.net/u8xR3sFT9qiEFs6ENRQYQn.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/u8xR3sFT9qiEFs6ENRQYQn.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: DJI / Future)</span></figcaption></figure><p>I’m a big fan of the DJI Mimo app, which has come a long way in the last few years. It’s now slick, feature-packed and fairly reliable (I still experience the odd issue connecting devices over Wi-Fi on my <a href="https://www.tomsguide.com/phones/iphones/iphone-17-pro-max-review"><u>iPhone 17 Pro Max</u></a>). The Mimo app is your central point of control for the Pocket 4, and used for registering your device, updating it, live camera control and editing.</p><p>I really like the editing functionality in particular. I roughly spliced together the vlogging video above in around an hour between meetings. I was able to cut and arrange clips, apply effects, animations and music, and generally put out a decent looking short video in very little time. Sure, it’s a bit clunkier to use than desktop editing software, but for creators editing on the go, it’s very good.</p><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-battery-life-overheating"><span>DJI Osmo Pocket 4 review: Battery life & overheating</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RLWRgkFNsLDYqDPURtcptQ" name="DJI_osmo_pocket4 16.JPG" alt="DJI Osmo Pocket 4 against a blue background" src="https://cdn.mos.cms.futurecdn.net/RLWRgkFNsLDYqDPURtcptQ.jpg" mos="" align="middle" fullscreen="" width="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>DJI rates the OP4 for 240 minutes, up from 166 minutes on the OP3. The caveat there, though, is that those figures are at 1080p. At 4K/60fps, which is where we test all our cameras (if they shoot at 60fps), you can expect less. Realistically, that’s where most people will want to shoot.</p><p>In real world testing at a mixture of 4K/60fps, 4K/30fps and 4K/240fps, I shot for around 80 minutes, recording around 40 short clips. The battery only dropped to around 50%, suggesting 160 minutes of runtime. </p><p>In my lab testing at 4K/60fps, the camera lasted for around 140 minutes, which is very impressive. What’s even more impressive is the thermal performance — the lab test is conducted indoors away from the cooling effects of wind and cold ambient temperatures. The OP4 got a little warm, but not hot, and didn’t overheat at all, recording in 4K/60fps continuously until the battery died.</p><h2 class="article-body__section" id="section-dji-osmo-pocket-4-review-verdict"><span>DJI Osmo Pocket 4 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="cadiBK3ZthWUHnGGAJFrqQ" name="DJI_osmo_pocket4 13.JPG" alt="DJI Osmo Pocket 4 against a blue background" src="https://cdn.mos.cms.futurecdn.net/cadiBK3ZthWUHnGGAJFrqQ.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>If you’re a vlogger, influencer or other content creator, especially one who records out and about regularly, there really is no better all-in-one package than the DJI Osmo Pocket 4. If you want beautiful, professional-looking video, with solid audio and a wealth of connectivity options, it’s <em>the</em> camera for you.</p><p>Sure, it’s a refinement of the Pocket 3 rather than a wholesale revamp, but as I mentioned in my intro, the Pocket 3 was already the content creator’s default tool for a reason: it was fantastically good at its job, so there wasn’t much that needed changing.</p><p>The Pocket 4 upgrades all the right things. Changes to the sensor, stabilization and battery life, plus the shedding of some unnecessary features to make way for more genuinely useful ones. DJI has knocked it out of the park… again.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ GoPro announces Mission 1 prices, and point and shoots are now in big trouble ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/gopro-action-cameras/gopro-announces-mission-1-prices-and-point-and-shoots-are-now-in-big-trouble</link>
                                                                            <description>
                            <![CDATA[ GoPro announced pricing for its new line of Mission 1 cameras, which make for attractive alternatives for point-and-shoot cams. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">2mAXqk4SuBusXinAgJRuaL</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/rqVHoxfQDLa6EpHU6B4Hpk-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 20 Apr 2026 20:37:49 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[GoPros &amp; Action Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></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/rqVHoxfQDLa6EpHU6B4Hpk-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide / John Velasco]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[GoPro Mission 1 hands-on.]]></media:description>                                                            <media:text><![CDATA[GoPro Mission 1 hands-on.]]></media:text>
                                <media:title type="plain"><![CDATA[GoPro Mission 1 hands-on.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/rqVHoxfQDLa6EpHU6B4Hpk-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>GoPro just <a href="https://investor.gopro.com/press-releases/press-release-details/2026/GoPro-Announces-Pricing-for-New-MISSION-1-Series-Professional-8K-and-4K-Open-Gate-Compact-Cinema-Cameras-Starting-at-499-for-Existing-GoPro-Subscribers/default.aspx">revealed pricing for its new Mission 1</a> action cameras and micro four thirds camera makers should be scared. </p><p>If you haven't seen the new Mission 1 lineup, GoPro describes the series as lightweight cinema cameras. They feature 50MP 1-inch sensors, a new processor, making them faster than even some of the<a href="https://www.tomsguide.com/us/best-phone-cameras,review-2272.html"> best camera phones</a> and a good number of point-and-shoots.</p><p>Of course, being GoPro, these cameras are video forward capable of filming up to 8K resolution footage. And it comes with a reasonable price tag.</p><ul><li>Mission 1 - $599.99</li><li>Mission 1 Pro - $699.99</li><li>Mission 1 Pro ILS - $699.99</li></ul><p>All three cameras come with $100 off discounts for existing GoPro subscribers and purchase through GoPro.com</p><p>The Mission 1 Pro is heralded as the flagship model in the new lineup, while the Mission 1 Pro ILS features a micro four thirds mount that can be used to attached different lens to achieve a cinematic look much like dedicated cinema cameras that cost thousands of dollars.</p><p>There's added versatility in these pocket cinema cameras because of the myriad of accessories that GoPro has designed with them, like a body cage where users can attach different accessories such as cold shoe mounts or a Wireless Mic kit.</p><div ><table><caption>Mission 1 Pro Editions</caption><thead><tr><th class="firstcol empty" ></th><th  ><p>Features</p></th><th  ><p>Price</p></th></tr></thead><tbody><tr><td class="firstcol " ><p>Grip Edition</p></td><td  ><p>Versatile grip, cold shoe mounts, 1/4-20, magentic latch mounting, mounting fingers</p></td><td  ><p>$799.99</p></td></tr><tr><td class="firstcol " ><p>Creator Edition</p></td><td  ><p>Mission 1 Series Media Mod, built-in microphone, Volta 2 battery grip, Wireless Mic Complete Kit</p></td><td  ><p>$1,099.99</p></td></tr><tr><td class="firstcol " ><p>Ultimate Creator</p></td><td  ><p>GoPro Fluid Pro AI gimbal, Wireless Mic Complete Kit, Light Mod 2, lighting, Mission 1 Series Media Mod, and others</p></td><td  ><p>$1,999.99</p></td></tr></tbody></table></div><p>The Mission 1, Mission 1 Pro, Mission, and the Mission 1 Pro Grip Edition are all available for pre-order starting today. Those cameras will start arriving on May 28, 2026.</p><p>The other two creator editions and the ILS model won't be available until Q3 of 2026, which would pin it somewhere between July and September this summer.</p><h2 id="a-good-argument-to-move-on-from-your-point-and-shoot">A good argument to move on from your point-and-shoot</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Uv3XTPSs3TwyEju5es9Bpk" name="GoPro Mission 1" alt="GoPro Mission 1 hands-on." src="https://cdn.mos.cms.futurecdn.net/Uv3XTPSs3TwyEju5es9Bpk.jpg" mos="" align="middle" fullscreen="" width="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 / John Velasco)</span></figcaption></figure><p>In early April, my colleague <a href="https://www.tomsguide.com/cameras-photography/i-saw-the-gopro-mission-1-and-this-new-action-camera-could-make-all-point-and-shoots-obsolete-heres-why">John Velasco got a chance to check out the Mission 1</a> cameras and came away quite impressed. At the time, he suggested that the Mission 1 would be competitive if it was priced around $500, which GoPro pretty much hits. </p><p>Even the high-end Pro and ILS models, make for compelling alternatives for traditional point-and-shoot cameras. This level of utility has neve been achieved by an action camera, which certainly would make buyers think harder about their next purchase. Not only do they record at 8K resolution, but they also offer insane 960 fps slow motion. For vloggers, it's an attractive pocketable camera that GoPro says will offer outstanding stabilization and low light performance.</p><p>"After seeing the GoPro Mission1 Pro ILS in particular, I’m convinced I could ditch my micro four thirds camera altogether. In fact, the Mission1 makes a compelling argument that the traditional point-and-shoot is finally obsolete," Velasco wrote.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/best-photos-i-ve-taken-on-cameras-reviewed-2025">I’ve reviewed nearly 30 cameras this year: these are the best photos I’ve taken on instant, film and mirrorless</a></li><li><a href="https://www.tomsguide.com/cameras-photography/i-test-compact-and-film-cameras-for-a-living-and-here-are-my-top-12-recommendations-for-the-summer">I test compact and film cameras for a living, and here are my top 12 recommendations for the summer</a></li><li><a href="https://www.tomsguide.com/cameras-photography/photography-hobby-beginner-cameras-i-recommend">Want to take up photography in 2026? These are the 5 beginner cameras I recommend</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I test compact and film cameras for a living, and here are my top 12 recommendations for the summer ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/i-test-compact-and-film-cameras-for-a-living-and-here-are-my-top-12-recommendations-for-the-summer</link>
                                                                            <description>
                            <![CDATA[ Summer is a great time of the year to hang out with friends and family and make and capture memories. Here are my favorite compact and film cameras for it. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">Twskfhe79Nf8HvrwJLa5Q8</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/evLP35qxjKP2eF7hJWVZfA-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 19 Apr 2026 09:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/evLP35qxjKP2eF7hJWVZfA-1280-80.jpg">
                                                            <media:credit><![CDATA[Edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Cameras on a beach]]></media:description>                                                            <media:text><![CDATA[Cameras on a beach]]></media:text>
                                <media:title type="plain"><![CDATA[Cameras on a beach]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/evLP35qxjKP2eF7hJWVZfA-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As the days get longer and we spend more time with our family and friends, I'm sure we're all looking forward to making new memories — and capturing them. The <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras</a> make photography easier and help you capture the perfect sun-kissed shots. I love photography, and I rarely leave the house without my camera, and I review all kinds of cameras for a living — film and compact included.</p><p>If you're looking to pick up a film or compact camera for the summer, don't worry: I've got you covered. I've picked my top 12 favorites for you, and these have been thoroughly tested and personally reviewed by yours truly.</p><p>Whether you're a professional photographer who wants a stripped-back film cam or a novice who wants a simple point-and-shoot, there's something here for everyone. From the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-half-review">Fujifilm X half</a> and the <a href="https://www.tomsguide.com/cameras-photography/kodak-ektar-h35-half-frame-review">Kodak Ektar H35</a>, to the <a href="https://www.tomsguide.com/cameras-photography/canon-powershot-v1-review">Canon PowerShot V1 </a>and the <a href="https://www.tomsguide.com/cameras-photography/camp-snap-cs-8-review">Camp Snap CS-8</a>, here are my top picks for the summer.</p>        <div class="featured_product_block featured_block_hero" data-id="8a09f19f-8d26-4d9d-ad4c-0d54c98dbc95">            <a href="https://www.amazon.com/KODAK-Recording-Simulation-Megapixels-Beginners/dp/B0FKBDT8FR/" data-model-name="Charmera" 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/DKfx2jEZhngd5iHahPZyHT.jpg" alt="Kodak Charmera"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Charmera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="b74af1c0-c267-4117-ac4c-b5657b7a96ba">            <a href="https://www.amazon.com/KODAK-PIXPRO-Friendly-Digital-Camera/dp/B0DY8BRMM5/" data-model-name="PIXPRO C1" 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/fHbenGQJ4FURFke6JkTGFT.jpg" alt="Kodak PIXPRO C1"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">PIXPRO C1</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="deb0b23a-471f-457f-a66f-5322e23f1e85">            <a href="https://www.amazon.com/FUJIFILM-Half-Premium-Compact-Camera/dp/B0F8X959L5/" data-model-name="X half" 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/XmAK6Zn8HWUx6TChcMCgET.jpg" alt="Fujifilm x Half"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Fujifilm</div>                                        <div class="featured__title">X half</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e437357b-227e-40fc-b23d-040de1601d34">            <a href="https://www.campsnapphoto.com/collections/camp-snap-screen-free-digital-cameras/products/screen-free-digital-camera-v103" data-model-name="Screen-Free" 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/BtBenzcEzPXz6wtJjj2pET.jpg" alt="Camp Snap"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Camp Snap</div>                                        <div class="featured__title">Screen-Free</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="40bf9f1b-fad7-4cab-a7b5-d36829942fd3">            <a href="https://www.campsnapphoto.com/products/cs8" data-model-name="CS-8" 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/aiJrhEmBiuvgYHZbP2T99T.jpg" alt="Camp Snap CS-8"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Camp Snap</div>                                        <div class="featured__title">CS-8</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="b2aa0564-8de0-4814-8feb-16d5fcc42649">            <a href="https://www.amazon.com/DJI-Stabilization-Rotatable-Touchscreen-Photography/dp/B0CG19QXWD/" data-model-name="Osmo Pocket 3" 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/Dq9EmzMBHTX42pfMssEJ9T.jpg" alt="DJI Osmo Pocket 3"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>DJI</div>                                        <div class="featured__title">Osmo Pocket 3</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="466ac7da-ef72-4aad-a704-dcb812bafbb6">            <a href="https://www.amazon.com/Canon-PowerShot-Ultra-Wide-Angle-Vlogging-Streaming/dp/B0F252G9JG/" data-model-name="PowerShot V1" 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/a5zryx7kLe2f8BcstiWFAT.jpg" alt="Canon PowerShot V1"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">PowerShot V1</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="990dc198-9ae4-4ab6-bc15-701146767c3f">            <a href="https://www.amazon.com/KODAK-Reusable-Focus-Free-Lightweight-24-Exposure/dp/B0CQRCXH7L/" data-model-name="Ektar H35 half-frame film camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jEusuTAzkELYfV8NKVyH9T.jpg" alt="Kodak Ektar H35"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Ektar H35 half-frame film camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="77d771d5-1cdc-4d53-92fe-dda5f7a5a1b0">            <a href="https://www.amazon.com/Kodak-Ultra-35mm-Film-Camera/dp/B09GNDCS7B/" data-model-name="Ultra F9 film camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/BJQvHw2xqnywGTLemgAqET.jpg" alt="Kodak Ultra F9"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>KODAK</div>                                        <div class="featured__title">Ultra F9 film camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="2a8a88c6-f6f5-4026-86b0-f56bc9a21728">            <a href="https://www.amazon.com/Pentax-captures-Vertical-Orientation-Perfect/dp/B0D7C449ZB/" data-model-name="17 half-frame film camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/CCc78BkztqrL9PRxYpX2ET.jpg" alt="Pentax 17"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Pentax</div>                                        <div class="featured__title">17 half-frame film camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="b24e4414-959f-4a4b-8152-a113cc88a478">            <a href="https://www.amazon.com/Fujifilm-QuickSnap-Disposable-Quality-Microfiber/dp/B07N8GCYQB/" data-model-name="Quicksnap Flash 400 Disposable 35mm film camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/7VK8kSQzGPQcQnhk27heAT.jpg" alt="Fujifilm QuickSnap Flash"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Fujifilm</div>                                        <div class="featured__title">Quicksnap Flash 400 Disposable 35mm film camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="c54c45e5-1ff2-4215-98e0-65ae028cfbb2">            <a href="https://shop.lomography.com/us/lomo-mc-a-35-mm-film-camera-black" data-model-name="Lomo MC-A 35mm film camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.33%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/PkjqfNzdtehLGYAE25tY8T.jpg" alt="Lomo MC-A"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>LOMOGRAPHY</div>                                        <div class="featured__title">Lomo MC-A 35mm film camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h2 id="how-do-i-choose-the-best-camera-for-me">How do I choose the best camera for me?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5FjioNf83E3AmZ9XemnF3o" name="Kodak_charmera 13.JPG" alt="A Kodak Charmera keychain camera" src="https://cdn.mos.cms.futurecdn.net/5FjioNf83E3AmZ9XemnF3o.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>So, you're spoilt for choice, but which compact or film camera should you <em>really </em>choose? They're all fantastic in their own right, but the first thing you need to consider is your <strong>budget</strong>. The <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a>, for instance, is the cheapest on the list, and then you've got the Canon PowerShot V1 which costs nearly $1,000. There are 10 other options to consider between the two, so think about how much you can spend comfortably.</p><p>Second, you need to think about the <strong>use-case</strong> for the camera you're going to buy. There are video, film and digital cameras on this list, so what are you planning on shooting this summer? Do you want to capture aesthetic, retro-looking photos, and you don't mind the fact that you can't see them before they get developed at a shop? Then a film camera like the <a href="https://www.tomsguide.com/cameras-photography/lomography-lomo-mc-a-35-mm-film-camera-review">Lomography Lomo MC-A 35mm</a> is right for you.</p><p>If you want more bang for your buck, get a half-frame film camera instead, like the Kodak Ektar H35 which captures two photos on one frame, essentially doubling the shot count. So if you're using a 36-count film roll, you'll get 72 photos in total.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="vRLyEPuhhp4tDWSRadBxxN" name="Camp_Snap_digital_super8_ 15.JPG" alt="A Camp Snap CS-8 video camera" src="https://cdn.mos.cms.futurecdn.net/vRLyEPuhhp4tDWSRadBxxN.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 what about being able to take still photos <em>and </em>record video? You've got two cameras here that I wholeheartedly recommend. There's the Camp Snap CS-8 which is inspired by Super 8s, and it captures 2.7K footage at either 30fps or 18fps, depending on the chosen color profile, and there are a bunch of aspect ratios to experiment with.</p><p>While the CS-8 is fantastic, you might want to consider the <a href="https://www.tomsguide.com/reviews/dji-osmo-pocket-3">DJI Osmo Pocket 3</a> instead for high-res 4K/60fps video. We gave the pocket-sized video camera 4.5-stars in our review, noting that it offers professionally stabilized video for a fraction of the price of a full gimbal and video camera setup. And it's super easy to use — oh, and you can take it to concerts and festivals where interchangeable lens cameras (ILCs) aren't permitted!</p><p>It's important to consider how many megapixels a camera offers, so make sure to check out my reviews for all the snappers mentioned. Which camera will you be using this summer? Let me know in the comments below!</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-exEVKW"></div>                            </div>                            <script src="https://kwizly.com/embed/exEVKW.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/us/best-cameras,review-2196.html">The best cameras in 2026</a></li><li><a href="https://www.tomsguide.com/tech/ive-spent-two-years-testing-300-cameras-earbuds-drones-keyboards-and-more-here-are-my-favorites-from-each-category">I've spent two years testing 300 cameras, earbuds, drones, keyboards and more — here are my favorites from each category</a></li><li><a href="https://www.tomsguide.com/cameras-photography/photography-hobby-beginner-cameras-i-recommend">Want to take up photography in 2026? These are the 5 beginner cameras I recommend</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Sony just announced the 2026 World Photography Awards results — here are the winners ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/sony-just-announced-the-2026-world-photography-awards-results-here-are-the-winners</link>
                                                                            <description>
                            <![CDATA[ Here are the winners from the Sony World Photography Awards 2026 — and how to enter next year's competition. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">yBh4TZraPisPChyCCd3BDV</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/JXbyTRGTAAyG5XDo7Si8U8-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 17 Apr 2026 09:10:20 +0000</pubDate>                                                                                                                                <updated>Fri, 17 Apr 2026 09:12:38 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras &amp; Photography]]></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/JXbyTRGTAAyG5XDo7Si8U8-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[The Sony WPA logo on a wall]]></media:description>                                                            <media:text><![CDATA[The Sony WPA logo on a wall]]></media:text>
                                <media:title type="plain"><![CDATA[The Sony WPA logo on a wall]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/JXbyTRGTAAyG5XDo7Si8U8-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Last night, April 16th, <a href="https://www.sony.co.uk/presscentre/sony-world-photography-awards-2026-overall-winners-announced" target="_blank"><u>Sony announced the winners and shortlist of its 2026 World Photography Awards</u></a>. Now into their 19th year, Sony’s World Photography Awards (WPA) celebrate and award existing and new photographic talent, giving its winners an international platform to showcase their work (not to mention cash prizes and a sack of Sony imaging gear).</p><p>This year, the WPAs received over 430,000 entrants from 200 countries across the world, making this one of (if not the) largest photography competition around.</p><p>The flagship award — the equivalent of ‘Best Actor / Actress’ at the Oscars, if you like — is Photographer of the Year. This year is has been scooped up by Citlali Fabián, a London-based visual artist originally from the Yalalteca Indigenous community, Mexico, whose work explores the intersections of identity with territory, migration and community. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:850px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="XACBWVXSsmBHsxC4fkAj23" name="SWPA Citlali Fabián" alt="A photo of a girl holding her fist in the air, with sketches of other children performing the same motion around her." src="https://cdn.mos.cms.futurecdn.net/XACBWVXSsmBHsxC4fkAj23.jpg" mos="" align="middle" fullscreen="" width="850" height="850" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">One of Citlali Fabián's winning images. </span><span class="credit" itemprop="copyrightHolder">(Image credit: © Citlali Fabián, Mexico, Photographer of the Year, Professional Competition, Creative, Sony World Photography Awards 2026)</span></figcaption></figure><p>The 2026 ‘Outstanding Contribution to Photography’ award — a lifetime achievement kinda deal — goes to legendary American street/documentary photographer Joel Meyerowitz. A pioneer of color photography, Joel was at Ground Zero to document the <a href="https://www.joelmeyerowitz.com/aftermath" target="_blank"><u>aftermath of the World Trade Center attacks</u></a> on 9/11 — a visceral and haunting collection that simultaneously highlights the heroism and humanistic elements of the tragedy and cleanup. </p><p>The WPAs award a further 10 professional categories, though, each with a winner and two runners up. Not only that, but Sony and the WPA also foster the next generation of photographic talent through student and youth categories, plus an open category for anyone to enter. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:850px;"><p class="vanilla-image-block" style="padding-top:66.71%;"><img id="W4r53CbJu86EnD8Ke73mUE" name="Seungho Kim" alt="A photo of a small child and a dog peaking over a table at a fried egg." src="https://cdn.mos.cms.futurecdn.net/W4r53CbJu86EnD8Ke73mUE.jpg" mos="" align="middle" fullscreen="" width="850" height="567" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">One of Seungho Kim's winning images. </span><span class="credit" itemprop="copyrightHolder">(Image credit: © Seungho Kim, Republic Of Korea, Winner, Professional Competition, Perspectives, Sony World Photography Awards 2026)</span></figcaption></figure><p>I've included some of my favorite shots from this year's finalists and winners, but a full gallery of all the competition images are all visible on the <a href="https://www.worldphoto.org/index%2Ephp/sony-world-photography-awards/winners-galleries" target="_blank"><u>World Photography Awards website</u></a>.</p><div ><table><thead><tr><th class="firstcol " ><p><strong>Category</strong></p></th><th  ><p><strong>Winner</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Photographer of the Year</strong></p></td><td  ><p>Citlali Fabián (Mexico)</p></td></tr><tr><td class="firstcol " ><p><strong>Architecture & Design</strong></p></td><td  ><p>Joy Saha (Bangladesh)</p></td></tr><tr><td class="firstcol " ><p><strong>Documentary</strong></p></td><td  ><p>Santiago Mesa (Colombia)</p></td></tr><tr><td class="firstcol " ><p><strong>Environment</strong></p></td><td  ><p>Isadora Romero (Ecuador)</p></td></tr><tr><td class="firstcol " ><p><strong>Landscape</strong></p></td><td  ><p>Dafna Talmor (U.K.)</p></td></tr><tr><td class="firstcol " ><p><strong>Perspectives</strong></p></td><td  ><p>Seungho Kim (Republic of Korea)</p></td></tr><tr><td class="firstcol " ><p><strong>Portraiture</strong></p></td><td  ><p>Jean-Marc Caimi & Valentina Piccinni (Italy)</p></td></tr><tr><td class="firstcol " ><p><strong>Sport</strong></p></td><td  ><p>Todd Antony (New Zealand)</p></td></tr><tr><td class="firstcol " ><p><strong>Still Life</strong></p></td><td  ><p>Vilma Taubo (Norway)</p></td></tr><tr><td class="firstcol " ><p><strong>Wildlife & Nature</strong></p></td><td  ><p>Will Burrard-Lucas (U.K.)</p></td></tr><tr><td class="firstcol " ><p><strong>Open</strong></p></td><td  ><p>Elle Leontiev (Australia)</p></td></tr><tr><td class="firstcol " ><p><strong>Student</strong></p></td><td  ><p>Jubair Ahmed Arnob (Bangladesh)</p></td></tr><tr><td class="firstcol " ><p><strong>Youth</strong></p></td><td  ><p>Philip Kangas (Sweden)</p></td></tr><tr><td class="firstcol " ><p><strong>Outstanding Contribution to Photography</strong></p></td><td  ><p>Joel Meyerowitz (U.S.)</p></td></tr></tbody></table></div><h2 id="london-exhibition">London exhibition</h2><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="wRUQvAmNojVYC2aw7jh3V8" name="IMG_0822" alt="A photo of four images in black and white hosted on an exhibition wall." src="https://cdn.mos.cms.futurecdn.net/wRUQvAmNojVYC2aw7jh3V8.jpg" mos="" align="middle" fullscreen="" width="5712" height="3213" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">A series by Hayate Kurisu, runner up in the Perspectives category. </span><span class="credit" itemprop="copyrightHolder">(Image credit: © Hayate Kurisu, Japan, Professional / Perspectives, 2nd Place, Sony World Photography Awards 2026)</span></figcaption></figure><p>If, like me, you’re lucky enough to be in London in mid-April, you can also check out the World Photography Awards exhibition, which runs from <a href="https://www.worldphoto.org/index%2Ephp/sony-world-photography-awards-exhibition"><u>April 17 to May 4 2026 at Somerset House, London</u></a> (tickets available via the link). </p><p>The exhibition will showcase over 300 entries from this year’s awards. I was given early access to the exhibition to see the entries, and it’s a fantastic showcase. The talent on display is phenomenal. My favorite series is by Seungho Kim, winner of the Perspectives category. His work documents his and his wife's lives after having an unexpected child, with a particular focus on how the family dog adjusted, and finally made friends with the baby. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:850px;"><p class="vanilla-image-block" style="padding-top:66.71%;"><img id="ngzqVSvDAat5GXjZqhzCdc" name="Seungho Kim, Sony World Photography Awards 2026" alt="A child eating a watermelon next to a dog." src="https://cdn.mos.cms.futurecdn.net/ngzqVSvDAat5GXjZqhzCdc.jpg" mos="" align="middle" fullscreen="" width="850" height="567" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: © Seungho Kim, Korea, Republic Of, Winner, Professional Competition, Perspectives, Sony World Photography Awards 2026)</span></figcaption></figure><p>I’ve interviewed many photographers in my time as a journalist, seen many exhibitions, and obviously gazed intently at many photos. I know full well that many photographers shoot deliberately with a final printed product in mind. There’s nothing like looking at an image in large printed format, in precisely the way the artist wanted it to be seen. It always beats digital in my book.</p><h2 id="no-sony-no-problem">No Sony? No problem</h2><p>If you’re inspired by this year’s awards — either the online gallery or physical exhibition — you can get involved in <a href="https://www.worldphoto.org/sony-world-photography-awards" target="_blank"><u>2027’s competition</u></a>, which will open on June 1 2026. </p><p>Importantly, while Sony hosts the WPA, there’s absolutely no stipulation for entries to be shot on a Sony camera. You don’t need to be rocking the latest, greatest <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-spent-a-day-shooting-on-the-new-sony-a7-v-here-are-my-first-impressions-spoiler-alert-its-great"><u>Sony A7V</u></a> to enter next year’s competition — maybe you’re rocking a humble <a href="https://www.tomsguide.com/reviews/canon-eos-r100"><u>Canon EOS R100</u></a> or, like me, a several-year-old <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-just-bought-this-5-year-old-fujifilm-camera-and-it-proves-newer-isnt-always-better"><u>Fujifilm X-E4</u></a>. In fact, I talked to lots of the entrants in this year's awards, and even many of the pros shoot with other brands. Will Burrard-Lucas, winner of the Wildlife & Nature category, shoots on whatever old DSLRs he can find to jerry rig into traps or mobile recording devices (he literally straps cameras to RC cars to get close up wide-angle photos of hyenas!)</p><p>And that’s cool: it means you don’t need expensive gear to take part in the event. Remember, great imagery has more to do with the photographer than the camera.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/ive-been-using-my-fujifilm-camera-for-almost-a-year-and-these-are-the-time-saving-photography-hacks-i-swear-by">I’ve been using my Fujifilm camera for almost a year and these are the time-saving photography hacks I swear by</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-spent-a-day-shooting-on-the-new-sony-a7-v-here-are-my-first-impressions-spoiler-alert-its-great">I spent a day shooting on the new Sony A7 V — here are my first impressions (spoiler alert: it's great)</a></li><li><a href="https://www.tomsguide.com/cameras-photography/i-swapped-out-my-modern-mirrorless-for-this-20-year-old-dslr-and-ive-never-been-more-creative">I swapped out my modern mirrorless for this 20-year-old DSLR — and I’ve never been more creative</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I saw the GoPro Mission 1 and this new action camera could make all point and shoots obsolete — here’s why ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/i-saw-the-gopro-mission-1-and-this-new-action-camera-could-make-all-point-and-shoots-obsolete-heres-why</link>
                                                                            <description>
                            <![CDATA[ The GoPro Mission 1 series is a move towards professional cinematography, featuring a massive 1-inch 50MP sensor and an interchangeable lens system. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">mEBfWx5mBCZ5hXRghFnsmD</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/5mVGPKfrc8AyQTapiKypmk-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 15 Apr 2026 07:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras &amp; Photography]]></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/5mVGPKfrc8AyQTapiKypmk-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&#039;s Guide / John Velasco]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[GoPro Mission 1 hands-on.]]></media:description>                                                            <media:text><![CDATA[GoPro Mission 1 hands-on.]]></media:text>
                                <media:title type="plain"><![CDATA[GoPro Mission 1 hands-on.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/5mVGPKfrc8AyQTapiKypmk-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I own all sorts of cameras for different circumstances, but over the last year, I’ve been trying to follow a "less is more" philosophy. That’s why I’ve stopped carrying a full-frame mirrorless camera to work, opting instead for my compact <a href="https://www.amazon.com/PANASONIC-Mirrorless-12-32mm-Megapixels-DC-GX850KK/dp/B01N6KNQV1/" target="_blank" rel="nofollow">Panasonic Lumix GX85</a>0 micro four thirds camera — though I still keep my phone and <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/gopro-lit-hero-review">GoPro Hero Lit</a> on me.</p><p>This is exactly why I’m so stoked for <a href="https://gopro.com/en/us/info/mission-1-learnmore" target="_blank">GoPro's new Mission1</a> lineup. It represents a dramatic shift, catering to serious enthusiasts who crave better performance and utility than what’s offered by the current action cam market. </p><p>After seeing the GoPro Mission1 Pro ILS in particular, I’m convinced I could ditch my micro four thirds camera altogether. In fact, the Mission1 makes a compelling argument that the traditional point-and-shoot is finally obsolete. Here’s why.</p><h2 id="a-1-inch-sensor-is-a-big-deal-than-you-think">A 1-inch sensor is a big deal than you think</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="rqVHoxfQDLa6EpHU6B4Hpk" name="GoPro Mission 1" alt="GoPro Mission 1 on top of a table." src="https://cdn.mos.cms.futurecdn.net/rqVHoxfQDLa6EpHU6B4Hpk.jpg" mos="" align="middle" fullscreen="" width="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 / John Velasco)</span></figcaption></figure><p>If you look at the current market for point-and-shoot cameras, many effectively use smartphone-sized sensors — and the <a href="https://www.tomsguide.com/cameras-photography/panasonic-lumix-zs99-tz99-review">Panasonic Lumix ZS99</a> is a perfect example. It actually has a smaller sensor than what’s found in the <a href="https://www.tomsguide.com/us/best-phone-cameras,review-2272.html">best camera phone</a> around, the <a href="https://www.tomsguide.com/phones/samsung-phones/samsung-galaxy-s26-ultra-review">Galaxy S26 Ultra</a>, which utilizes a 1/1.3-inch main sensor.</p><p>That’s why the GoPro Mission 1 lineup's move to a 50MP 1-inch sensor is such a significant shift. By using a larger sensor, the camera is able to accumulate more light, resulting in sharper images and vastly improved low-light performance. Most flagship phones now excel at shooting in the dark, but they still require long exposure times. Since the Mission 1 models all feature 1-inch sensors, they can gather the same amount of light in a fraction of the time required by standard phones or point-and-shoots.</p><p>However, the GoPro Mission1 is clearly positioned as a video camera first — which makes that 1-inch sensor even more valuable. If you’ve ever shot video with a point-and-shoot or a phone in dim lighting, you know how noisy and muddy the footage can look. With the Mission 1, that should no longer be the case.</p><h2 id="size-does-matter">Size does matter</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="T5iDRdTvdGuYT73NMiamCm" name="GoPro Mission 1" alt="GoPro Mission 1 Pro ILS next to the Panasonic Lumix GX850" src="https://cdn.mos.cms.futurecdn.net/T5iDRdTvdGuYT73NMiamCm.jpg" mos="" align="middle" fullscreen="" width="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 / John Velasco)</span></figcaption></figure><p>Micro four thirds systems are some of the lightest mirrorless cameras around, but even my Panasonic Lumix GX850 looks bulky next to the GoPro Mission 1. In a side-by-side comparison, it's unbelieveable to see the size difference between them. The beauty here is that GoPro has taken the iconic design of its Hero 13 Black and made it ever so slightly larger to house everything necessary to make the Mission 1 the ultimate all-purpose camera.</p><p>Thanks to its rugged construction and small footprint, it’s far easier to position in tight areas where traditional cameras simply won’t fit — like on my car's dashboard. Crucially, it offers significantly better protection than any point-and-shoot camera, complete with every mounting option imaginable: the standard 1/4-20 tripod mount, classic folding fingers, and my personal favorite, the Magnetic Latch Mount.</p><p>Even with its diminutive size, GoPro claims the Mission 1 offers impressive endurance thanks to the new Enduro 2 battery, which is rated for three hours of 4K 30fps recording.</p><h2 id="a-truly-pocketable-cinema-cam">A truly pocketable cinema cam</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="aEZyCBXE8cDoiz5mMZ2o7m" name="GoPro Mission 1" alt="GoPro Mission 1 Pro ILS next to the Panasonic Lumix GX850" src="https://cdn.mos.cms.futurecdn.net/aEZyCBXE8cDoiz5mMZ2o7m.jpg" mos="" align="middle" fullscreen="" width="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 / John Velasco)</span></figcaption></figure><p>I never imagined that a GoPro-sized camera could deliver the same level of cinematic quality as full-frame cameras like my Lumix S5. GoPro’s answer to that skepticism is the Mission 1 Pro ILS, which features a Micro Four Thirds mount that allows you to attach professional cinema glass directly to the body. While it shares the same 50MP 1-inch sensor as the other two models, the video specs are in a league of their own — we're talking 8K 60fps, 4K 120fps, and beyond.</p><p>While there's roughly a 3x crop factor with this system that narrows the field of view, the fact that it’s an interchangeable lens system still means I have total control over the compression and aesthetic of my shots. I’m especially excited about its 960 fps slow motion mode at 1080p, which rivals dedicated high-speed cameras costing tens of thousands of dollars. Imagine the cinematic possibilities when pairing a lens with a shallow depth of field with that level of frame rate.</p><p>Time wouldn't just slow down, it would practically stand still.</p><h2 id="forget-point-and-shoots">Forget point-and-shoots</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Uv3XTPSs3TwyEju5es9Bpk" name="GoPro Mission 1" alt="GoPro Mission 1 on top of a table." src="https://cdn.mos.cms.futurecdn.net/Uv3XTPSs3TwyEju5es9Bpk.jpg" mos="" align="middle" fullscreen="" width="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 / John Velasco)</span></figcaption></figure><p>All of this has me incredibly excited for the GoPro Mission 1 lineup. You’ll have to consider what matters most to your workflow because there are three options: the entry-level Mission 1, the flagship Mission 1 Pro, and the interchangeable Mission 1 Pro ILS.</p><p>GoPro hasn’t announced official pricing yet, but pre-orders begin on May 21, 2026. I’ve been considering an aftermarket compact micro four thirds camera similar to my Lumix GX850, or even a bridge point-and-shoot, but the insane prices I’m seeing on the used market make it impossible to pull the trigger.</p><p>That’s why GoPro can’t afford to overprice this lineup. If I had to guess, a competitive price point for the Standard Mission 1 would be around $500 — especially since the GoPro Hero13 Black currently retails for $429. As for the Mission 1 Pro and Mission 1 Pro ILS, I’m hoping the latter stays under $899 to keep it a more attractive option than a traditional mirrorless setup.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eJ7kKW"></div>                            </div>                            <script src="https://kwizly.com/embed/eJ7kKW.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/drones/i-test-drones-for-a-living-heres-what-i-think-of-reddits-top-recommendations-for-beginners">I test drones for a living — here’s what I think of Reddit’s top recommendations for beginners</a></li><li><a href="https://www.tomsguide.com/cameras-photography/drones/my-top-rated-drones-for-the-summer">Summer is the best time for aerial photography — here are my top-rated drones for the perfect shots</a></li><li><a href="https://www.tomsguide.com/cameras-photography/drones/dji-avata-360-versus-antigravity-a1-clear-winner">I've flown both the DJI Avata 360 and the Antigravity A1 360-degree drones — and there's a clear winner</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ DJI announces new Lito drone, possibly the last one coming to the US ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/drones/dji-announces-new-lito-drone-possibly-the-last-one-coming-to-the-us</link>
                                                                            <description>
                            <![CDATA[ The DJI Lito has appeared in FCC filings suggesting it could be the last new drone released in the US by the company. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">S8YHvocJncPsnPR4MPU8AF</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/RhBNBu5rrqpSg2y88xHtWh-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Tue, 14 Apr 2026 21:57:24 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Drones]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></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/png" url="https://cdn.mos.cms.futurecdn.net/RhBNBu5rrqpSg2y88xHtWh-1280-80.png">
                                                            <media:credit><![CDATA[DJI]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[DJI Mini 2 SE]]></media:description>                                                            <media:text><![CDATA[DJI Mini 2 SE]]></media:text>
                                <media:title type="plain"><![CDATA[DJI Mini 2 SE]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/RhBNBu5rrqpSg2y88xHtWh-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>DJI has faced a ban on importing and selling drones in the United States since December 2025, but the company may have slipped one final drone under the wire. Between leaks, official announcements and FCC filings DJI may have one more drone for American enthusiasts to enjoy.</p><p>Today, DJI posted a teaser video for the new Lito drone, which could be the last DJI model to receive FCC certification, pending the outcomes of <a href="https://www.tomsguide.com/cameras-photography/drones/fcc-exceeded-its-statutory-authority-claims-dji-in-lawsuit-to-overturn-drone-ban">various legal battles</a>. The teaser promised an official reveal on April 23. </p><div class="instagram-embed"><blockquote class="instagram-media"  data-instgrm-version="6" style="width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><p><a href="https://www.instagram.com/p/DXHIBHCAcUa/" target="_blank">A post shared by DJI (@djiglobal)</a></p><p>A photo posted by  on </p></blockquote></div><p>Based on <a href="https://apps.fcc.gov/oetcf/eas/reports/ViewExhibitReport.cfm?mode=Exhibits&RequestTimeout=500&calledFromFrame=N&application_id=dh2YkDBdTZ21Cdrr9w7PQA%3D%3D&fcc_id=SS3-DGN12" target="_blank">FCC filings</a>, it appears DJI was able to get the Lito approved before the December 22, 2025, cutoff for the FCC ban. This means the new drone is "grandfathered" into the US market and can be sold in the US.</p><h2 id="what-is-dji-lito">What is DJI Lito?</h2><p>DJI's official teaser only highlighted the name and official launch date. However, some previous leaks revealed images that purported to be the new compact drone. </p><p>Earlier this month, "tech detective" Igor Bogdanov<a href="https://x.com/Quadro_News/status/2039951432846082443" target="_blank"> showed photos</a> that look like marketing materials and not just CAD renders or dummy models. He also <a href="https://x.com/Quadro_News/status/2040874012461404520" target="_blank">posted some packaging images</a>. Today, in light of the announcement, he <a href="https://x.com/Quadro_News/status/2044093551563129050" target="_blank">posted some zoomed in</a> looks at the drone.</p><div class="see-more see-more--clipped"><blockquote class="twitter-tweet hawk-ignore" data-lang="en"><p lang="en" dir="ltr">👉Well, well, well, what's this in my hands. The new DJI Lito🤭#djilito1 #djilitox1 pic.twitter.com/fp8tgxORBD<a href="https://twitter.com/cantworkitout/status/2039951432846082443">April 3, 2026</a></p></blockquote><div class="see-more__filter"></div></div><p>A<a href="https://www.reddit.com/r/dji/comments/1sae4ai/dji_lito_1_and_lito_x1/" target="_blank"> separate leak on Reddit</a> appears to reveal some specs of the Lito drone. According to the poster, the Lito has a top speed of 18m/s and an apparent incompatibility with current controllers.</p><p>Based solely on Igor's images, the Lito looks a lot like the<a href="https://www.tomsguide.com/cameras-photography/drones/dji-mini-5-pro-review"> DJI Mini 5 Pro</a>, if perhaps a bit sleeker. It's not clear where it will fit in the DJI lineup. My speculation is that the Lito is a slightly less feature-rich version of the Mini.</p><h2 id="how-we-got-here">How we got here</h2><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="2h3muDgz4HntnDRUn9mpcL" name="DJI Flip - Hero" alt="DJI Flip - Hero" src="https://cdn.mos.cms.futurecdn.net/2h3muDgz4HntnDRUn9mpcL.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: Future)</span></figcaption></figure><p>As mentioned, the FCC implemented a ban on foreign-made drones in December of last year, specifically targeting Chinese manufacturers. DJI has been fighting the ban since at least 2023 and <a href="https://www.tomsguide.com/cameras-photography/drones/fcc-exceeded-its-statutory-authority-claims-dji-in-lawsuit-to-overturn-drone-ban">sued the FCC</a> in February claiming the agency "exceeded its statutory authority" in an attempt to overturn the ban.</p><p>You can <a href="https://www.tomsguide.com/cameras-photography/drones/dji-drones-still-available-to-buy-usa-february-26">still buy DJI-made drones</a> in the US, especially grandfathered in ones that were made before the December deadline. Everything else has been or will be added to the FCC's "Covered List" or its list of banned devices.</p><p>Technically, DJI could possibly release more drones in the US after <a href="https://www.tomsguide.com/cameras-photography/drones/us-drone-ban-update">passing "rigorous cyber and hardware assessments,"</a> according to the <a href="https://www.fcc.gov/files/nsd-fcc-covered-list-waiver-0126" target="_blank" rel="nofollow">NSD FCC Covered List Waiver 0126</a>. However, it's not clear what those assessments include and if they'll be made difficult just to keep drones on the Covered List.</p><p>The legal battles are ongoing, so it may be some time before this is totally cleared up. But for now, the Lito is coming and could be the last new DJI drone we see for a while.</p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/drones/i-test-drones-for-a-living-heres-what-i-think-of-reddits-top-recommendations-for-beginners">I test drones for a living — here’s what I think of Reddit’s top recommendations for beginners</a></li><li><a href="https://www.tomsguide.com/cameras-photography/drones/my-top-rated-drones-for-the-summer">Summer is the best time for aerial photography — here are my top-rated drones for the perfect shots</a></li><li><a href="https://www.tomsguide.com/cameras-photography/drones/dji-avata-360-versus-antigravity-a1-clear-winner">I've flown both the DJI Avata 360 and the Antigravity A1 360-degree drones — and there's a clear winner</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve been using my Fujifilm camera for almost a year and these are the time-saving photography hacks I swear by ]]></title>
                                                                                                                                                                                                <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/ive-been-using-my-fujifilm-camera-for-almost-a-year-and-these-are-the-time-saving-photography-hacks-i-swear-by</link>
                                                                            <description>
                            <![CDATA[ As someone who reviews cameras for a living and uses her Fujifilm X-T50 nearly every day, here are 5 tips and tricks that save me time. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ZDYfxzqVYL8itZxPBqjdEE</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/4eSwV5mssP4ppK89H3HCk5-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 05 Apr 2026 10:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Mirrorless Cameras]]></category>
                                                    <category><![CDATA[Cameras &amp; Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.png ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Nikita is a Senior Writer on the Reviews team at Tom&#039;s Guide. She is a lifelong gaming and photography enthusiast, especially interested in wildlife photography. Having worked as a Sub Editor and Writer for Canon EMEA, she’s a bit of a grammar nerd (and a supporter of the Oxford comma), and has also interviewed photographers from all over the world and working in different genres. A holder of two master’s degrees, the most recent one being in Magazine Journalism from Cardiff University, Nikita’s work has appeared in several publications such as Motor Sport Magazine, NME, Marriott Bonvoy, The Independent, and Metro. Her favorite tech includes the PS5, the DJI Air 3S, and the Fujifilm X-T50. She&#039;s also a licensed drone pilot and cameras expert so you&#039;ll find her testing those nearly every week.&lt;/p&gt;&lt;p&gt;In her downtime, Nikita can usually be found sinking hours into RPGs on her PS5, flying a drone, out on a walk with a camera in hand, at a concert, watching F1, or planning her next tattoo. You can follow her photography account on Instagram&lt;a href=&quot;https://www.instagram.com/photos.bynikita/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt; here&lt;/a&gt;.&lt;/p&gt; ]]></dc:description>
                                                                                                                                <cf:isSponsored>false</cf:isSponsored>
                <cf:hasAffiliateLinks>false</cf:hasAffiliateLinks>
                <cf:isPaid>false</cf:isPaid>
                                                                                                                                <media:content type="image/jpeg" url="https://cdn.mos.cms.futurecdn.net/4eSwV5mssP4ppK89H3HCk5-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A person holding a Fujifilm X-T50 mirrorless camera]]></media:description>                                                            <media:text><![CDATA[A person holding a Fujifilm X-T50 mirrorless camera]]></media:text>
                                <media:title type="plain"><![CDATA[A person holding a Fujifilm X-T50 mirrorless camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/4eSwV5mssP4ppK89H3HCk5-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I adore photography. Whenever I go to a new place, or even when I'm out and about with my family or partner, I love photographing the landscapes, wildlife, and taking portraits. For almost a year, I've been using the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-t50-review">Fujifilm X-T50</a>, and I've taken some beautiful photos with it — in the U.K., in Iceland, and everywhere else I've been since owning it.</p><p>One of the greatest things about the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a> is that they can simplify your workflow and make it easier to take photos. The X-T50, like many of Fuji's latest cameras, boasts time-saving features which you may or may not already know of. When you're photographing a crucial moment, say, a bird in flight, you might not want to bother with diving into the in-depth menu settings.</p><div class="product"><a data-dimension112="214fa35e-b573-4d27-aba8-e0d3bbf8aa45" data-action="Deal Block" data-label="The Fujifilm X-T50 packs a punch with a huge 40.2MP sensor, excellent in-body image stabilization and reliable autofocus. It's also the first Fuji camera to sport a dedicated film simulation dial on its body for maximum (and easy-to-reach) creativity." data-dimension48="The Fujifilm X-T50 packs a punch with a huge 40.2MP sensor, excellent in-body image stabilization and reliable autofocus. It's also the first Fuji camera to sport a dedicated film simulation dial on its body for maximum (and easy-to-reach) creativity." data-dimension25="$1399" href="https://www.amazon.com/Fujifilm-Mirrorless-Digital-Camera-XF16-50mmF2-8-4-8/dp/B0D3X8LLNT/" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1264px;"><p class="vanilla-image-block" style="padding-top:78.01%;"><img id="HbwuHEixisNVZfUN754aZi" name="X-T50 deal body" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/HbwuHEixisNVZfUN754aZi.png" mos="" align="middle" fullscreen="" width="1264" height="986" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Fujifilm X-T50 packs a punch with a huge 40.2MP sensor, excellent in-body image stabilization and reliable autofocus. It's also the first Fuji camera to sport a dedicated film simulation dial on its body for maximum (and easy-to-reach) creativity.<a class="view-deal button" href="https://www.amazon.com/Fujifilm-Mirrorless-Digital-Camera-XF16-50mmF2-8-4-8/dp/B0D3X8LLNT/" target="_blank" rel="nofollow" data-dimension112="214fa35e-b573-4d27-aba8-e0d3bbf8aa45" data-action="Deal Block" data-label="The Fujifilm X-T50 packs a punch with a huge 40.2MP sensor, excellent in-body image stabilization and reliable autofocus. It's also the first Fuji camera to sport a dedicated film simulation dial on its body for maximum (and easy-to-reach) creativity." data-dimension48="The Fujifilm X-T50 packs a punch with a huge 40.2MP sensor, excellent in-body image stabilization and reliable autofocus. It's also the first Fuji camera to sport a dedicated film simulation dial on its body for maximum (and easy-to-reach) creativity." data-dimension25="$1399">View Deal</a></p></div><p>Luckily, many modern Fuji cameras — and other brands — offer quick and easy solutions. From creating your own custom film recipes to adjusting shooting settings on the fly, here are my time-saving tips and tricks as someone who reviews cameras for a living. Hopefully you'll find some of these helpful, and if there are any that you use and I haven't included, do share them in the comments below!</p><h2 id="1-creating-custom-film-recipes">1. Creating custom film recipes</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5VNjbh2Qv7KpcBTny7ZTqJ" name="Fujifilm_XT50_16.JPG" alt="A Fujifilm X-T50 camera in the charcoal grey colorway" src="https://cdn.mos.cms.futurecdn.net/5VNjbh2Qv7KpcBTny7ZTqJ.jpg" mos="" align="middle" fullscreen="" width="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>Film simulation dials are a staple on Fuji's latest cameras, but it was first introduced on the Fujifilm X-T50. While some people may disagree, I absolutely love the film sim dial and think it's a big time-saver. The film sim dial gives you access to <strong>eight of Fuji's most popular recipes, as well as three customizable slots and a 'C' position for menu-based selection</strong> — so 20 in total.</p><p>The film sim dial basically lets you apply filters so that images are ready to go for either printing or sharing on social media without you having to edit them in post-production. Customizing your own recipes is super easy too, by <strong>diving into the image quality settings menu and editing custom settings</strong>. You can then adjust values like Grain Effect, Color Chrome Effect, White Balance, ISO, Sharpness, Exposure Compensation, and more.</p><p>It's worth taking your time and playing with the settings to get to the look you like most — or you can copy settings from recipes available on Google and online forums, like Reddit. After that, it's a matter of assigning them to either the three custom slots or menu-based selection.</p><h2 id="2-assigning-personal-shortcuts">2. Assigning personal shortcuts</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pLykasDUvFtHVogR55ubCK" name="Fujifilm_XT50_15.JPG" alt="A Fujifilm X-T50 camera in the charcoal grey colorway" src="https://cdn.mos.cms.futurecdn.net/pLykasDUvFtHVogR55ubCK.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>We've all been there. You want to change one setting in a camera's menu settings but you have to navigate through several sub-menus that aren't relevant to you in that moment. The Fujifilm X-T50, for one, offers a simple solution: My Menu. The last tab in the main menu settings is empty by default, but <strong>you can add up to 10 items you use most</strong> (but aren't assigned to physical buttons).</p><p>Once you've assigned your desired items to My Menu and it's all set up, the camera will default to this tab every time you press the Menu button. This saves you a lot of time as you negate having to scroll through sections like Image Quality.</p><h2 id="3-quickly-resetting-the-focus-point">3. Quickly resetting the focus point</h2><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="zKPCSEAi9rea4tJmFPqQaH" name="Fujifilm_XT50_08.JPG" alt="A Fujifilm X-T50 camera in the charcoal grey colorway" src="https://cdn.mos.cms.futurecdn.net/v2/t:245,l:385,cw:960,ch:540,q:80/zKPCSEAi9rea4tJmFPqQaH.jpg" mos="" align="middle" fullscreen="" width="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 Fujifilm X-T50, like many other Fuji cameras, features a focus joystick, located on the back panel and next to the monitor. While you can use the joystick to change focus points, did you know that <strong>pressing it resets and recenters the focus point</strong>? This is something most photographers will be familiar with, but I don't think you should overlook its importance.</p><p>I find myself pressing the focus joystick to recenter the focus point nearly every time I pick up my camera. It's quick and easy and saves you the time you'd otherwise spend on manually bringing the focus point back to the center of the screen.</p><h2 id="4-zooming-into-the-focus-point">4. Zooming into the focus point</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1379px;"><p class="vanilla-image-block" style="padding-top:56.27%;"><img id="twSdv9iRuJS56iDD4wmckG" name="Fujifilm_XT50_07.JPG" alt="A Fujifilm X-T50 camera in the charcoal grey colorway" src="https://cdn.mos.cms.futurecdn.net/v2/t:141,l:307,cw:1379,ch:776,q:80/twSdv9iRuJS56iDD4wmckG.jpg" mos="" align="middle" fullscreen="" width="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>One of my favorite dials on the Fujifilm X-T50 is the rear command dial, or the thumb wheel, located right above the focus joystick and navigation buttons. If you use it as a scroll wheel, you can adjust the ISO — or the shutter speed and other settings, depending on what you've programmed it to. <strong>Clicking it, however, makes the electronic viewfinder (EVF) and the LDC screen show a 10x zoom on your focus point</strong>.</p><p>It's a quick way of checking if your subject is in focus, especially if you're using manual focus or single AF. To zoom out, simply press it again. If you're shooting through the viewfinder, as I usually do, you can verify sharpness without taking your eye off the EVF.</p><h2 id="5-remapping-gestures-buttons-on-the-fly">5. Remapping gestures/buttons on the fly</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:613px;"><p class="vanilla-image-block" style="padding-top:56.28%;"><img id="QYSuYtuGKnsYK29S4qeFUG" name="Fujifilm_XT50_03.JPG" alt="A Fujifilm X-T50 camera in the charcoal grey colorway" src="https://cdn.mos.cms.futurecdn.net/v2/t:621,l:1030,cw:613,ch:345,q:80/QYSuYtuGKnsYK29S4qeFUG.jpg" mos="" align="middle" fullscreen="" width="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 rear panel of the Fujifilm X-T50 houses a few different buttons which help you navigate the camera's settings and gallery. But did you know that the Disp/Back button isn't just for, well, exiting menus? If you <strong>hold the Disp/Back button for two seconds, it instantly opens the Function (Fn) Setting menu</strong>, which allows you to remap any physical buttons and touch gestures on the fly. </p><p>Using the Disp/Back button saves you a lot of time you'd otherwise spend diving into detailed menus. It's important to note that touchscreen gestures are disabled by default, so remember to enable them via the menu if you want to use swipes on the screen to access settings like internal mic adjustment, histogram, and so on.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OzLRPe"></div>                            </div>                            <script src="https://kwizly.com/embed/OzLRPe.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-didnt-realize-how-much-i-loved-the-fujifilm-x-t50-until-i-photographed-my-trip-to-iceland-and-it-blew-me-away">I didn’t realize how much I loved the Fujifilm X-T50 until I photographed my trip to Iceland — and it blew me away</a></li><li><a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-t50-review">Fujifilm X-T50 review: Fuji's mid-range stunner</a></li><li><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-think-the-new-fujifilm-x-e5-is-overrated-and-im-so-glad-i-bought-this-camera-instead">I think the new Fujifilm X-E5 is overrated — and I’m so glad I bought this camera instead</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
            </channel>
</rss>