<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:dcterms="http://purl.org/dc/terms/"
     xmlns:media="http://search.yahoo.com/mrss/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:cf="https://www.futureplc.com/rss/content-flags"
>
    <channel>
                    <atom:link href="https://www.tomsguide.com/feeds/tag/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, 04 Sep 2026 14:07:12 +0000</lastBuildDate>
                            <language>en</language>
                                <item>
                                                            <title><![CDATA[ The Lola Miffy Digital Camera is the cutest camera I’ve ever tested — and it’s just so fun for everyday photography ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Cameras don’t all have to look serious, and the Lola Miffy Digital Camera proves it. A Miffy-themed design, a Miffy-themed friendship bracelet/camera strap, and Miffy-themed filters make this the cutest camera I’ve ever tested. It’s easy to use, making it great for casual or travel photography, and it captures 8MP images as well as 4K video. Battery life is solid, too, lasting up to three days on a single charge.</p><p>But the Lola Miffy struggles from slight shutter lag which often results in missed shots, and given its tiny sensor, it struggles in dimly lit environments. And while you get a 20x digital zoom, image quality drops significantly as you zoom in.</p><p>Is this still the <a href="https://www.tomsguide.com/us/best-cheap-cameras,review-6319.html">best cheap camera</a> for you? Read my full Lola Miffy Digital Camera review to find out.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-specs"><span>Lola Miffy Digital Camera review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://lola.camera/products/lola-digital-camera-miffy" target="_blank" rel="nofollow">$109</a> / <a href="https://www.urbanoutfitters.com/en-gb/shop/lola-x-miffy-digital-camera" target="_blank" rel="nofollow">£79</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>8MP CMOS</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>None</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Not specified</p></td></tr><tr><td class="firstcol " ><p><strong>Focal length</strong></p></td><td  ><p>26mm wide-angle</p></td></tr><tr><td class="firstcol " ><p><strong>Focus range</strong></p></td><td  ><p>Not specified</p></td></tr><tr><td class="firstcol " ><p><strong>Aperture</strong></p></td><td  ><p>f/2.4</p></td></tr><tr><td class="firstcol " ><p><strong>Zoom</strong></p></td><td  ><p>20x digital</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>2.8-inch IPS Color screen</p></td></tr><tr><td class="firstcol " ><p><strong>ISO range</strong></p></td><td  ><p>80-1600</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>HD 720P | 4K 2160P | 2.7K</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>USB-C, 1x SD card</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless/Bluetooth</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Max shutter speed</strong></p></td><td  ><p>Not specified</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>2 to 3 days</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>3.75 x 2.25 x 0.75 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>8oz (including battery and card)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-price-availability"><span>Lola Miffy Digital Camera 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="qF8cUegiYRMYAoqZSeuoYX" name="LOLAXMIFFY_digicam_.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/qF8cUegiYRMYAoqZSeuoYX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Launched in July 2026, the Lola Miffy Digital Camera is a special edition of the base Lola Digital Camera, and it retails for <a href="https://lola.camera/products/lola-digital-camera-miffy" target="_blank" rel="nofollow">$109 at Lola U.S.</a> / <a href="https://www.urbanoutfitters.com/en-gb/shop/lola-x-miffy-digital-camera" target="_blank" rel="nofollow">£79 at Urban Outfitters U.K.</a> It’s priced similarly to the <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-c1-compact-camera-review">Kodak PIXPRO C1</a> ($99) and the <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-fz45-review">Kodak PIXPRO FZ45</a> ($99).</p><p>The Lola Miffy is a little pricier than other compact cameras we’ve tested, like the <a href="https://www.tomsguide.com/cameras-photography/camp-snap-2-review">Camp Snap 2</a> ($69) and 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> ($99), but these are screen-free cameras that don’t let you see your shots until you’re at your computer. In its essence, the Lola Miffy is similar to the <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a> ($30) — it’s a hobby camera and a fashion accessory. The Miffy edition is Lola’s second-ever collaboration, the first being with Betty Boop. While the Lola Miffy won’t capture award-winning photos, I still think it’s worth the money because of how darn cute it is.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-design"><span>Lola Miffy Digital Camera 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="rY7TnhbfVscEj5jjKqSqeX" name="LOLAXMIFFY_digicam_ 11.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/rY7TnhbfVscEj5jjKqSqeX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lola Miffy Digital Camera is extremely portable and pocketable, making it perfect for your travels and everyday photography. The camera measures 3.75 x 2.25 x 0.75 inches and weighs about 8oz (including battery and card), and I had no problem carrying it around in my trousers’ pocket. I even took it to a musical festival and again, didn’t feel weighed down by it. It’s easy to use with one or both hands, too. It’s heavier than the 3.8oz Camp Snap 2 but the tradeoff is better build quality (more on that shortly).</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9XDbEsAR4o2CJ2tWCr9rcX" name="LOLAXMIFFY_digicam_ 9.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/9XDbEsAR4o2CJ2tWCr9rcX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 Lola Miffy’s biggest selling points is its adorable design — it’s the cutest camera or gadget I’ve ever used, really. As its name suggests, Miffy, the adorable cartoon bunny I grew up watching, adorns the front, along with four gold stars. The camera is wrapped in a lovely shade of blue. A Miffy-themed friendship charm bracelet is attached to the camera and it functions as a strap for wrapping the camera around your wrist. You can also buy a Miffy-themed pouch at checkout. Overall, the design is 10/10, no notes. One of my friends took one look at it and said, “I’m buying it. Right now.” So there’s that.</p><p>Build quality is decent, too. The camera is made entirely of plastic and surprisingly, it doesn’t feel extremely plasticky or cheap, unlike the Kodak PIXPRO C1 and the Camp Snap 2.</p><h2 id="monitor">Monitor</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fbNevYXU4p63QqN24gyCaX" name="LOLAXMIFFY_digicam_ 2.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/fbNevYXU4p63QqN24gyCaX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Similar to other cheap compact cameras I’ve tested, the Lola Miffy doesn’t feature a viewfinder so you’ll be relying on the 2.8-inch IPS Color screen for framing your shots. Lola doesn’t specify the screen’s resolution, but I found it easy to read in most lighting conditions. It got a little tricky under direct sunlight, though.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:75.00%;"><img id="rtbeD4Tgrc2YjNa2m5YFoR" name="Lola-Miffy-sample-9.JPG" alt="A photo taken on a Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/rtbeD4Tgrc2YjNa2m5YFoR.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1920" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/rtbeD4Tgrc2YjNa2m5YFoR.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 Lola Miffy’s screen doesn’t flip out or up, like on the Kodak PIXPRO C1, making it difficult to take selfies, if that’s your jam. The camera does feature a small mirror on the front, though, which you can use to frame your selfies. It takes some trial and error, but it’s handy to have. The selfie above was taken using the mirror for framing, and it’s turned out great.</p><h2 id="ports">Ports</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RCCjRyeMerUcZ579BNqtfX" name="LOLAXMIFFY_digicam_ 8.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/RCCjRyeMerUcZ579BNqtfX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 a lot in terms of ports. This is a very stripped-back camera, and there’s just one USB-C port on the Lola Miffy. This can be used to charge the camera or to transfer photos to your laptop. If you want to transfer photos directly to your phone while you’re on the move, you can add the Lola Mobile SD Card Adapter at checkout.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RJCVHR8JRryBLA22hV4AcX" name="LOLAXMIFFY_digicam_ 7.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/RJCVHR8JRryBLA22hV4AcX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>On the camera’s right-hand edge, you’ll find the slot for the SD card as well as the battery pack. Unlike on the Kodak PIXPRO C1, the Lola Miffy’s battery and SD card are protected by a sliding plastic cover.</p><h2 id="weather-sealing">Weather-sealing</h2><p>The Lola Miffy isn’t weather-sealed, just like the Camp Snap 2, so I wouldn’t recommend using it in very rainy or dusty conditions. I captured a few snaps when it started drizzling at a music festival and the camera survived, but again, exercise caution and do so at your own risk. If you want a rugged camera that’s weather-proof and can even survive a few tumbles, I’d recommend spending extra on the <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-wpz2-review">Kodak PIXPRO WPZ2</a> ($179).</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-controls"><span>Lola Miffy Digital Camera 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="z9FfwFyEwZzJkkcLsyNvcX" name="LOLAXMIFFY_digicam_ 4.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/z9FfwFyEwZzJkkcLsyNvcX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lola Miffy Digital Camera is extremely easy to use, thanks to its straightforward controls. The shutter button is located next to the power button on the top plate, and half-pressing the former makes the camera focus on your subject. You’ll find your standard navigation buttons next to the monitor on the back, as well as “W” and “T” buttons for zooming in and 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="QMQgsf4bHVNeFPMFiwddVX" name="LOLAXMIFFY_digicam_ 3.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/QMQgsf4bHVNeFPMFiwddVX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The buttons give good tactile feedback, and the camera’s menu system is straightforward and easy to understand. Pressing the left navigation button while shooting lets you cycle through 20 filters and Miffy-themed frames, too, which is fun and lets you get creative with your shots.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hSxihexwSxFcsFdekidWcX" name="LOLAXMIFFY_digicam_ 10.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/hSxihexwSxFcsFdekidWcX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 thing to note, though, is that there’s a one-second lag between pressing the shutter button and capturing the image. Given that the shutter takes a second to engage, it resulted in me missing a few shots — or telling my friends to hold their pose for longer. This is similar to what we’ve seen on the Camp Snap 2 and the Camp Snap CS-Pro. While not a dealbreaker, it can be quite annoying.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-lens"><span>Lola Miffy Digital Camera review: Lens</span></h2><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="qF8cUegiYRMYAoqZSeuoYX" name="LOLAXMIFFY_digicam_.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/v2/t:291,l:645,cw:960,ch:540,q:80/qF8cUegiYRMYAoqZSeuoYX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lola Miffy Digital Camera is fitted with an 8MP CMOS sensor with a focal length of 26mm — just like the Kodak PIXPRO C1. This focal length is wide enough for landscape shots, arm-length selfies, group photos, and casual travel photography. Lola doesn’t specify the camera’s focal range, and there’s no dedicated macro mode like on the Kodak PIXPRO C1. I found it enough to take photos of my friends and landscapes, though.</p><p>One of the Lola Miffy’s headline features is its 20x digital zoom which, in theory, lets you take close-up shots of subjects without physically getting close to them. But because you only have 8MP at your disposal, you quickly lose pixels as you zoom in, and there’s a noticeable downgrade in image quality, too (more on this shortly).</p><p>The Lola Miffy’s lens has a fixed f/2.4 aperture. On paper, that’s wide enough to let a lot of light into the sensor, and capture good images in low-light conditions. However, because the camera’s sensor is quite small, images taken in dimly lit environments generally come out noisy and low-quality. This is similar to the Kodak PIXPRO C1’s f/2 aperture and the camera’s low-light performance.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-image-quality"><span>Lola Miffy Digital Camera review: Image quality</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/oceDReg468B6DV9rcLsfLT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QFrytKk5RM8DYkcuWtit6S.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/apCNS93hbj3hbp5EEKb4JT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/KRfm4XXXbPLg6feq9miCJT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gRHseei7fapqvZuzSWXmKT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JDhMWrspef82edBGBeqw8S.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EDXWtxeEQufpGFpetVWFMT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MPmfX4vHNnTaSxzimww9LT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MbMfjsBWyZP5LaT9Qe3JJT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9hVy4TcHxAvd5jAMGddHWS.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LtoMPYsbWtqJsDYjMLHPHT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The Lola Miffy Digital Camera is designed for anyone who wants to capture memories — which is exactly what I did. I photographed a music festival and my daily life in Bath, U.K., and I’m pleased with the photos. Because the resolution is limited to 8MP, the photos aren’t the best quality, but they have a nice retro touch to them, as you can see above.</p><p>I love the colors the camera has captured. While the greens look slightly washed out in a few shots, that’s what I was expecting from a cheap compact camera anyway, so I don’t mind. The edges of the buildings and bridge look sharp, as does the photo of my partner. The camera’s autofocus performs surprisingly well, too, and I found it quick enough to focus on humans and birds.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/NnxeGh86xzSjNraAeNzSNS.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/h9PjwxBLDW8Q3eqTbAverb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/WghsEqEyeHuPWPzxjLfdrb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EqgXr4hBuMuHAvnCGm2tpb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/aowRDwE5yiSXWdBTh3Xypb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/cE5ufaWmC4LYzwU5j9UfAb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/SjafVyrvNxvyponUH8hoob.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/erust7H9y7GMTU5AjMsEib.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9jgemp8QeYhEcHonhcxJZb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Nzp5GRMYN6qPdizaySFEXb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EEiPUUGvgpn8j6uYN3ZKPb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nHApTBZ5eYVaocWmXxFjHa.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/NkRzKB9BGUcjf4CkTeJy2b.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QgRjRZBhzYtDRSJtpkZhGb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/AveD2q2kgFFA7iXuSaSXKb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/SzRgj3Huv7D9XAC7q5KSGa.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/To9d2BgLTn3JfxqYKwxYCa.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>As I mentioned before, you get access to 20 Miffy-themed frames and filters, examples of which you can see above. These can be applied when you’re taking the shot by pressing the left navigation button — they can’t be applied retrospectively. I’m sure this will please fans of the Dutch fictional bunny, especially millennials (like myself) who grew up watching her. There are color filters, too, like red, cyan, black and white, and more, and these can help you get creative shots.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ha3oEz3kVK77gLnZE2CAER.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/55SimpGy9yBpRVVHQUcoVS.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/3E2VmmPA3EadY5N7JhfGCR.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pkanLsWWevKb9fuscCiJiR.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>So, how about that 20x digital zoom? On paper, it sounds very handy as it can help you get close-up shots of distant subjects without moving physically yourself. While it’s useful, the image quality deteriorates rapidly. In the gallery above, the first shot was taken at 20x zoom and the second is the same photo but zoomed all the way out — and it’s the same for the second series of the church.</p><p>The drummer in the first shot and the church clock in the third are blurry, pixelated and the edges aren’t well-defined. They aren’t usable at all, if I’m being honest. So while the zoom is nice to have, it doesn’t add much.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:75.00%;"><img id="TjfFLT7GY87yB4ZVFQLJ9S" name="Lola-Miffy-sample-44" alt="A photo taken on a Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/TjfFLT7GY87yB4ZVFQLJ9S.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1920" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/TjfFLT7GY87yB4ZVFQLJ9S.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 Lola Miffy’s low-light performance is nothing to write home about either. On a mirrorless camera, f/2.4 aperture is generally fantastic as it lets more light into the sensor, and enables you to get better shots in dimly lit environments — not on the Lola Miffy, though, as the sensor is just too small.</p><p>The image above was taken in a dark room, with just a small light source to the right of the shelving unit. As you can see, it’s extremely noisy and grainy. It appears washed out and not at all what it looked like to my naked eye. This is similar to the Kodak PIXPRO C1’s and the Kodak Charmera’s low-light performance.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-video-quality"><span>Lola Miffy Digital Camera review: Video quality</span></h2><div class="looped-video"><video class="lazyload-in-view lazyloading" data-src="https://cdn.mos.cms.futurecdn.net/RSGutHpcjZLjfWFyiJY5MJ/Lola%20Miffy%20Digital%20Camera%204K.mp4" autoplay loop muted playsinline src="https://cdn.mos.cms.futurecdn.net/RSGutHpcjZLjfWFyiJY5MJ/Lola%20Miffy%20Digital%20Camera%204K.mp4"></video></div><p>Alongside capturing stills, the Lola Miffy Digital Camera can record HD 720P, 4K, and 2.7K video — although Lola doesn’t specify frame rates. You can watch a compilation of 4K clips above. While the colors have come out nicely, with a retro tinge akin to the images, the clips are quite choppy, which I’m guessing is because of a low frame rate.</p><p>The quality reminds me of the home videos my parents made of me when I was a child — or like these videos were recorded on the <a href="https://www.tomsguide.com/cameras-photography/camp-snap-cs-8-review">Camp Snap CS-8</a> ($139). The camera is sufficient enough when it comes to recording videos for fun, but not for serious vlogging.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-battery-life"><span>Lola Miffy Digital Camera 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="cLxVWGWsQ54unhaFjsqnLX" name="LOLAXMIFFY_digicam_ 6.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/cLxVWGWsQ54unhaFjsqnLX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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’ve tested many, many cameras as a reviews writer, from the expensive <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-q3-43-review">Leica Q3 43</a> ($7,380) to the super cheap <a href="https://www.tomsguide.com/cameras-photography/lomography-simple-use-reloadable-film-camera-classicolor-review">Lomography Simple Use</a> ($27), but I’ve never seen a brand describe a camera’s battery life quite like Lola. The Lola Miffy Digital Camera is rated for two to three days of use. Lola doesn’t specify the number of shots a single charge can yield, or the optimal shooting conditions for extending battery life. Just two to three days of vibes.</p><p>That’s not necessarily a bad thing as the Lola Miffy is designed for casual photographers, and people who want a camera just for fun. “Two to three days,” then, makes a lot of sense, as that’s what the camera’s target audience will most care about. In my testing, the camera lasted me just under three days of use, having used it extensively at a two-day music festival. I found the battery indicator reliable, too, as it steadily went from green to yellow to red.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-verdict"><span>Lola Miffy Digital Camera 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="oFYr4UtxB2VqoWV2prsowW" name="LOLAXMIFFY_digicam_ 1.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/oFYr4UtxB2VqoWV2prsowW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want a fun, no-frills digicam to capture everyday life, take to music festivals or on your travels, the Lola Miffy Digital Camera easily warrants a recommendation. It’s 10/10, Miffy-themed design is one of its biggest selling points, and it captures good images with a retro aesthetic, too. You also get 4K video capabilities, and an array of Miffy-themed frames and filters for creative shots. Battery life is strong, and the camera itself is extremely easy to use.</p><p>But as a cheap compact camera, the Lola Miffy isn’t without its faults. There’s slight shutter lag which can result in missed shots, and low-light performance isn’t great. But if neither of those sound like dealbreakers to you, and all you want is a portable camera that doubles as a fashion accessory, the Lola Miffy should be your port of call. It’s become a permanent part of my day bag now.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/lola-miffy-digital-camera-review</link>
                                                                            <description>
                            <![CDATA[ The Lola Miffy Digital Camera captures fun, retro-style 8MP photos, and it features Miffy-themed filters. It sports a cute design and is extremely easy to use. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">QvrVtcM4ALnGS6HW48pemR</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/qF8cUegiYRMYAoqZSeuoYX-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 04 Sep 2026 14:07:12 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/qF8cUegiYRMYAoqZSeuoYX-1280-80.jpg">
                                                            <media:credit><![CDATA[Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A Miffy-themed Lola Miffy Digital Camera]]></media:description>                                                            <media:text><![CDATA[A Miffy-themed Lola Miffy Digital Camera]]></media:text>
                                <media:title type="plain"><![CDATA[A Miffy-themed Lola Miffy Digital Camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/qF8cUegiYRMYAoqZSeuoYX-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Cameras don’t all have to look serious, and the Lola Miffy Digital Camera proves it. A Miffy-themed design, a Miffy-themed friendship bracelet/camera strap, and Miffy-themed filters make this the cutest camera I’ve ever tested. It’s easy to use, making it great for casual or travel photography, and it captures 8MP images as well as 4K video. Battery life is solid, too, lasting up to three days on a single charge.</p><p>But the Lola Miffy struggles from slight shutter lag which often results in missed shots, and given its tiny sensor, it struggles in dimly lit environments. And while you get a 20x digital zoom, image quality drops significantly as you zoom in.</p><p>Is this still the <a href="https://www.tomsguide.com/us/best-cheap-cameras,review-6319.html">best cheap camera</a> for you? Read my full Lola Miffy Digital Camera review to find out.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-specs"><span>Lola Miffy Digital Camera review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://lola.camera/products/lola-digital-camera-miffy" target="_blank" rel="nofollow">$109</a> / <a href="https://www.urbanoutfitters.com/en-gb/shop/lola-x-miffy-digital-camera" target="_blank" rel="nofollow">£79</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>8MP CMOS</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>None</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Not specified</p></td></tr><tr><td class="firstcol " ><p><strong>Focal length</strong></p></td><td  ><p>26mm wide-angle</p></td></tr><tr><td class="firstcol " ><p><strong>Focus range</strong></p></td><td  ><p>Not specified</p></td></tr><tr><td class="firstcol " ><p><strong>Aperture</strong></p></td><td  ><p>f/2.4</p></td></tr><tr><td class="firstcol " ><p><strong>Zoom</strong></p></td><td  ><p>20x digital</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>2.8-inch IPS Color screen</p></td></tr><tr><td class="firstcol " ><p><strong>ISO range</strong></p></td><td  ><p>80-1600</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>HD 720P | 4K 2160P | 2.7K</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>USB-C, 1x SD card</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless/Bluetooth</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Max shutter speed</strong></p></td><td  ><p>Not specified</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life</strong></p></td><td  ><p>2 to 3 days</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>3.75 x 2.25 x 0.75 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>8oz (including battery and card)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-price-availability"><span>Lola Miffy Digital Camera 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="qF8cUegiYRMYAoqZSeuoYX" name="LOLAXMIFFY_digicam_.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/qF8cUegiYRMYAoqZSeuoYX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Launched in July 2026, the Lola Miffy Digital Camera is a special edition of the base Lola Digital Camera, and it retails for <a href="https://lola.camera/products/lola-digital-camera-miffy" target="_blank" rel="nofollow">$109 at Lola U.S.</a> / <a href="https://www.urbanoutfitters.com/en-gb/shop/lola-x-miffy-digital-camera" target="_blank" rel="nofollow">£79 at Urban Outfitters U.K.</a> It’s priced similarly to the <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-c1-compact-camera-review">Kodak PIXPRO C1</a> ($99) and the <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-fz45-review">Kodak PIXPRO FZ45</a> ($99).</p><p>The Lola Miffy is a little pricier than other compact cameras we’ve tested, like the <a href="https://www.tomsguide.com/cameras-photography/camp-snap-2-review">Camp Snap 2</a> ($69) and 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> ($99), but these are screen-free cameras that don’t let you see your shots until you’re at your computer. In its essence, the Lola Miffy is similar to the <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a> ($30) — it’s a hobby camera and a fashion accessory. The Miffy edition is Lola’s second-ever collaboration, the first being with Betty Boop. While the Lola Miffy won’t capture award-winning photos, I still think it’s worth the money because of how darn cute it is.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-design"><span>Lola Miffy Digital Camera 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="rY7TnhbfVscEj5jjKqSqeX" name="LOLAXMIFFY_digicam_ 11.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/rY7TnhbfVscEj5jjKqSqeX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lola Miffy Digital Camera is extremely portable and pocketable, making it perfect for your travels and everyday photography. The camera measures 3.75 x 2.25 x 0.75 inches and weighs about 8oz (including battery and card), and I had no problem carrying it around in my trousers’ pocket. I even took it to a musical festival and again, didn’t feel weighed down by it. It’s easy to use with one or both hands, too. It’s heavier than the 3.8oz Camp Snap 2 but the tradeoff is better build quality (more on that shortly).</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9XDbEsAR4o2CJ2tWCr9rcX" name="LOLAXMIFFY_digicam_ 9.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/9XDbEsAR4o2CJ2tWCr9rcX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 Lola Miffy’s biggest selling points is its adorable design — it’s the cutest camera or gadget I’ve ever used, really. As its name suggests, Miffy, the adorable cartoon bunny I grew up watching, adorns the front, along with four gold stars. The camera is wrapped in a lovely shade of blue. A Miffy-themed friendship charm bracelet is attached to the camera and it functions as a strap for wrapping the camera around your wrist. You can also buy a Miffy-themed pouch at checkout. Overall, the design is 10/10, no notes. One of my friends took one look at it and said, “I’m buying it. Right now.” So there’s that.</p><p>Build quality is decent, too. The camera is made entirely of plastic and surprisingly, it doesn’t feel extremely plasticky or cheap, unlike the Kodak PIXPRO C1 and the Camp Snap 2.</p><h2 id="monitor">Monitor</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fbNevYXU4p63QqN24gyCaX" name="LOLAXMIFFY_digicam_ 2.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/fbNevYXU4p63QqN24gyCaX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Similar to other cheap compact cameras I’ve tested, the Lola Miffy doesn’t feature a viewfinder so you’ll be relying on the 2.8-inch IPS Color screen for framing your shots. Lola doesn’t specify the screen’s resolution, but I found it easy to read in most lighting conditions. It got a little tricky under direct sunlight, though.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:75.00%;"><img id="rtbeD4Tgrc2YjNa2m5YFoR" name="Lola-Miffy-sample-9.JPG" alt="A photo taken on a Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/rtbeD4Tgrc2YjNa2m5YFoR.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1920" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/rtbeD4Tgrc2YjNa2m5YFoR.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 Lola Miffy’s screen doesn’t flip out or up, like on the Kodak PIXPRO C1, making it difficult to take selfies, if that’s your jam. The camera does feature a small mirror on the front, though, which you can use to frame your selfies. It takes some trial and error, but it’s handy to have. The selfie above was taken using the mirror for framing, and it’s turned out great.</p><h2 id="ports">Ports</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RCCjRyeMerUcZ579BNqtfX" name="LOLAXMIFFY_digicam_ 8.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/RCCjRyeMerUcZ579BNqtfX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 a lot in terms of ports. This is a very stripped-back camera, and there’s just one USB-C port on the Lola Miffy. This can be used to charge the camera or to transfer photos to your laptop. If you want to transfer photos directly to your phone while you’re on the move, you can add the Lola Mobile SD Card Adapter at checkout.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="RJCVHR8JRryBLA22hV4AcX" name="LOLAXMIFFY_digicam_ 7.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/RJCVHR8JRryBLA22hV4AcX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>On the camera’s right-hand edge, you’ll find the slot for the SD card as well as the battery pack. Unlike on the Kodak PIXPRO C1, the Lola Miffy’s battery and SD card are protected by a sliding plastic cover.</p><h2 id="weather-sealing">Weather-sealing</h2><p>The Lola Miffy isn’t weather-sealed, just like the Camp Snap 2, so I wouldn’t recommend using it in very rainy or dusty conditions. I captured a few snaps when it started drizzling at a music festival and the camera survived, but again, exercise caution and do so at your own risk. If you want a rugged camera that’s weather-proof and can even survive a few tumbles, I’d recommend spending extra on the <a href="https://www.tomsguide.com/cameras-photography/kodak-pixpro-wpz2-review">Kodak PIXPRO WPZ2</a> ($179).</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-controls"><span>Lola Miffy Digital Camera 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="z9FfwFyEwZzJkkcLsyNvcX" name="LOLAXMIFFY_digicam_ 4.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/z9FfwFyEwZzJkkcLsyNvcX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lola Miffy Digital Camera is extremely easy to use, thanks to its straightforward controls. The shutter button is located next to the power button on the top plate, and half-pressing the former makes the camera focus on your subject. You’ll find your standard navigation buttons next to the monitor on the back, as well as “W” and “T” buttons for zooming in and 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="QMQgsf4bHVNeFPMFiwddVX" name="LOLAXMIFFY_digicam_ 3.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/QMQgsf4bHVNeFPMFiwddVX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The buttons give good tactile feedback, and the camera’s menu system is straightforward and easy to understand. Pressing the left navigation button while shooting lets you cycle through 20 filters and Miffy-themed frames, too, which is fun and lets you get creative with your shots.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="hSxihexwSxFcsFdekidWcX" name="LOLAXMIFFY_digicam_ 10.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/hSxihexwSxFcsFdekidWcX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 thing to note, though, is that there’s a one-second lag between pressing the shutter button and capturing the image. Given that the shutter takes a second to engage, it resulted in me missing a few shots — or telling my friends to hold their pose for longer. This is similar to what we’ve seen on the Camp Snap 2 and the Camp Snap CS-Pro. While not a dealbreaker, it can be quite annoying.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-lens"><span>Lola Miffy Digital Camera review: Lens</span></h2><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="qF8cUegiYRMYAoqZSeuoYX" name="LOLAXMIFFY_digicam_.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/v2/t:291,l:645,cw:960,ch:540,q:80/qF8cUegiYRMYAoqZSeuoYX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lola Miffy Digital Camera is fitted with an 8MP CMOS sensor with a focal length of 26mm — just like the Kodak PIXPRO C1. This focal length is wide enough for landscape shots, arm-length selfies, group photos, and casual travel photography. Lola doesn’t specify the camera’s focal range, and there’s no dedicated macro mode like on the Kodak PIXPRO C1. I found it enough to take photos of my friends and landscapes, though.</p><p>One of the Lola Miffy’s headline features is its 20x digital zoom which, in theory, lets you take close-up shots of subjects without physically getting close to them. But because you only have 8MP at your disposal, you quickly lose pixels as you zoom in, and there’s a noticeable downgrade in image quality, too (more on this shortly).</p><p>The Lola Miffy’s lens has a fixed f/2.4 aperture. On paper, that’s wide enough to let a lot of light into the sensor, and capture good images in low-light conditions. However, because the camera’s sensor is quite small, images taken in dimly lit environments generally come out noisy and low-quality. This is similar to the Kodak PIXPRO C1’s f/2 aperture and the camera’s low-light performance.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-image-quality"><span>Lola Miffy Digital Camera review: Image quality</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/oceDReg468B6DV9rcLsfLT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QFrytKk5RM8DYkcuWtit6S.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/apCNS93hbj3hbp5EEKb4JT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/KRfm4XXXbPLg6feq9miCJT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gRHseei7fapqvZuzSWXmKT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JDhMWrspef82edBGBeqw8S.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EDXWtxeEQufpGFpetVWFMT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MPmfX4vHNnTaSxzimww9LT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MbMfjsBWyZP5LaT9Qe3JJT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9hVy4TcHxAvd5jAMGddHWS.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LtoMPYsbWtqJsDYjMLHPHT.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The Lola Miffy Digital Camera is designed for anyone who wants to capture memories — which is exactly what I did. I photographed a music festival and my daily life in Bath, U.K., and I’m pleased with the photos. Because the resolution is limited to 8MP, the photos aren’t the best quality, but they have a nice retro touch to them, as you can see above.</p><p>I love the colors the camera has captured. While the greens look slightly washed out in a few shots, that’s what I was expecting from a cheap compact camera anyway, so I don’t mind. The edges of the buildings and bridge look sharp, as does the photo of my partner. The camera’s autofocus performs surprisingly well, too, and I found it quick enough to focus on humans and birds.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/NnxeGh86xzSjNraAeNzSNS.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/h9PjwxBLDW8Q3eqTbAverb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/WghsEqEyeHuPWPzxjLfdrb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EqgXr4hBuMuHAvnCGm2tpb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/aowRDwE5yiSXWdBTh3Xypb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/cE5ufaWmC4LYzwU5j9UfAb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/SjafVyrvNxvyponUH8hoob.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/erust7H9y7GMTU5AjMsEib.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9jgemp8QeYhEcHonhcxJZb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Nzp5GRMYN6qPdizaySFEXb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EEiPUUGvgpn8j6uYN3ZKPb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nHApTBZ5eYVaocWmXxFjHa.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/NkRzKB9BGUcjf4CkTeJy2b.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QgRjRZBhzYtDRSJtpkZhGb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/AveD2q2kgFFA7iXuSaSXKb.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/SzRgj3Huv7D9XAC7q5KSGa.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/To9d2BgLTn3JfxqYKwxYCa.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>As I mentioned before, you get access to 20 Miffy-themed frames and filters, examples of which you can see above. These can be applied when you’re taking the shot by pressing the left navigation button — they can’t be applied retrospectively. I’m sure this will please fans of the Dutch fictional bunny, especially millennials (like myself) who grew up watching her. There are color filters, too, like red, cyan, black and white, and more, and these can help you get creative shots.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ha3oEz3kVK77gLnZE2CAER.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/55SimpGy9yBpRVVHQUcoVS.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/3E2VmmPA3EadY5N7JhfGCR.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pkanLsWWevKb9fuscCiJiR.jpg" alt="A photo taken on a Lola Miffy Digital Camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>So, how about that 20x digital zoom? On paper, it sounds very handy as it can help you get close-up shots of distant subjects without moving physically yourself. While it’s useful, the image quality deteriorates rapidly. In the gallery above, the first shot was taken at 20x zoom and the second is the same photo but zoomed all the way out — and it’s the same for the second series of the church.</p><p>The drummer in the first shot and the church clock in the third are blurry, pixelated and the edges aren’t well-defined. They aren’t usable at all, if I’m being honest. So while the zoom is nice to have, it doesn’t add much.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2560px;"><p class="vanilla-image-block" style="padding-top:75.00%;"><img id="TjfFLT7GY87yB4ZVFQLJ9S" name="Lola-Miffy-sample-44" alt="A photo taken on a Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/TjfFLT7GY87yB4ZVFQLJ9S.jpg" mos="" align="middle" fullscreen="1" width="2560" height="1920" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/TjfFLT7GY87yB4ZVFQLJ9S.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 Lola Miffy’s low-light performance is nothing to write home about either. On a mirrorless camera, f/2.4 aperture is generally fantastic as it lets more light into the sensor, and enables you to get better shots in dimly lit environments — not on the Lola Miffy, though, as the sensor is just too small.</p><p>The image above was taken in a dark room, with just a small light source to the right of the shelving unit. As you can see, it’s extremely noisy and grainy. It appears washed out and not at all what it looked like to my naked eye. This is similar to the Kodak PIXPRO C1’s and the Kodak Charmera’s low-light performance.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-video-quality"><span>Lola Miffy Digital Camera review: Video quality</span></h2><div class="looped-video"><video class="lazyload-in-view lazyloading" data-src="https://cdn.mos.cms.futurecdn.net/RSGutHpcjZLjfWFyiJY5MJ/Lola%20Miffy%20Digital%20Camera%204K.mp4" autoplay loop muted playsinline src="https://cdn.mos.cms.futurecdn.net/RSGutHpcjZLjfWFyiJY5MJ/Lola%20Miffy%20Digital%20Camera%204K.mp4"></video></div><p>Alongside capturing stills, the Lola Miffy Digital Camera can record HD 720P, 4K, and 2.7K video — although Lola doesn’t specify frame rates. You can watch a compilation of 4K clips above. While the colors have come out nicely, with a retro tinge akin to the images, the clips are quite choppy, which I’m guessing is because of a low frame rate.</p><p>The quality reminds me of the home videos my parents made of me when I was a child — or like these videos were recorded on the <a href="https://www.tomsguide.com/cameras-photography/camp-snap-cs-8-review">Camp Snap CS-8</a> ($139). The camera is sufficient enough when it comes to recording videos for fun, but not for serious vlogging.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-battery-life"><span>Lola Miffy Digital Camera 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="cLxVWGWsQ54unhaFjsqnLX" name="LOLAXMIFFY_digicam_ 6.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/cLxVWGWsQ54unhaFjsqnLX.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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’ve tested many, many cameras as a reviews writer, from the expensive <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-q3-43-review">Leica Q3 43</a> ($7,380) to the super cheap <a href="https://www.tomsguide.com/cameras-photography/lomography-simple-use-reloadable-film-camera-classicolor-review">Lomography Simple Use</a> ($27), but I’ve never seen a brand describe a camera’s battery life quite like Lola. The Lola Miffy Digital Camera is rated for two to three days of use. Lola doesn’t specify the number of shots a single charge can yield, or the optimal shooting conditions for extending battery life. Just two to three days of vibes.</p><p>That’s not necessarily a bad thing as the Lola Miffy is designed for casual photographers, and people who want a camera just for fun. “Two to three days,” then, makes a lot of sense, as that’s what the camera’s target audience will most care about. In my testing, the camera lasted me just under three days of use, having used it extensively at a two-day music festival. I found the battery indicator reliable, too, as it steadily went from green to yellow to red.</p><h2 class="article-body__section" id="section-lola-miffy-digital-camera-review-verdict"><span>Lola Miffy Digital Camera 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="oFYr4UtxB2VqoWV2prsowW" name="LOLAXMIFFY_digicam_ 1.JPG" alt="A Miffy-themed Lola Miffy Digital Camera" src="https://cdn.mos.cms.futurecdn.net/oFYr4UtxB2VqoWV2prsowW.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want a fun, no-frills digicam to capture everyday life, take to music festivals or on your travels, the Lola Miffy Digital Camera easily warrants a recommendation. It’s 10/10, Miffy-themed design is one of its biggest selling points, and it captures good images with a retro aesthetic, too. You also get 4K video capabilities, and an array of Miffy-themed frames and filters for creative shots. Battery life is strong, and the camera itself is extremely easy to use.</p><p>But as a cheap compact camera, the Lola Miffy isn’t without its faults. There’s slight shutter lag which can result in missed shots, and low-light performance isn’t great. But if neither of those sound like dealbreakers to you, and all you want is a portable camera that doubles as a fashion accessory, the Lola Miffy should be your port of call. It’s become a permanent part of my day bag now.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I photographed eagles, falcons and owls with the new Fujifilm 400mm telephoto lens — and Fuji should take my money already ]]></title>
                                                                                                <dc:content><![CDATA[ <p>It's not very often that I get to photograph birds of prey in flight, or hold them — so when Fujifilm invited me to a pre-launch event for its new lens, I jumped at the opportunity. Today, on 4 September 2026, Fujifilm announced the new Fujinon XF 400mm F4.5 R LM OIS WR weather-sealed telephoto lens, designed for wildlife, birding and sports.</p><p>A couple of days ago, I went hands-on with it to capture portraits and action shots of falcons, eagles and owls. Though the weather was far from ideal, with the skies pouring down on us as soon as the lovely ladies from U.K.-based <a href="https://www.walkswithhawks.co.uk/" target="_blank" rel="nofollow">Walks with Hawks</a> brought out birds of prey, it was an enjoyable day out.</p><div class="product"><a data-dimension112="83a7af38-a838-11f1-8269-6f15b53251e0" data-action="Deal Block" data-label="XF 400mm F4.5 R LM OIS WR" data-dimension48="XF 400mm F4.5 R LM OIS WR" data-dimension25="$3199" href="https://www.adorama.com/fujifilm-fujinon-xf-400-mm-f-4-5-r-lm-ois-wr-lens/p/ifj40045" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1074px;"><p class="vanilla-image-block" style="padding-top:69.46%;"><img id="8Z5y44jjyTFhz8zzEGS4HJ" name="Fujinon XF 400mm" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8Z5y44jjyTFhz8zzEGS4HJ.png" mos="" align="middle" fullscreen="" width="1074" height="746" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR is a compact, lightweight telephoto lens for wildlife, birding, and sports. Its extended focal length provides 609mm equivalent reach for wildlife, and you get up to 5.5 stops of optical image stabilization, too.<a class="view-deal button" href="https://www.adorama.com/fujifilm-fujinon-xf-400-mm-f-4-5-r-lm-ois-wr-lens/p/ifj40045" target="_blank" rel="nofollow" data-dimension112="83a7af38-a838-11f1-8269-6f15b53251e0" data-action="Deal Block" data-label="XF 400mm F4.5 R LM OIS WR" data-dimension48="XF 400mm F4.5 R LM OIS WR" data-dimension25="$3199">View Deal</a></p></div><p>I was given the 400mm lens and a <a href="https://www.tomsguide.com/reviews/fujifilm-xh2s">Fujifilm X-H2S</a> and I had a lot of fun with it. Here's everything I photographed with the kit.</p><h2 id="a-long-reaching-lightweight-tele-lens">A long-reaching, lightweight tele lens</h2><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="WBfZoDCxhpdGF4XCmvhnrj" name="Fujifilm_400mm_lens-7" alt="A Fujifilm Fujinon XF 4400mm F4.5 R LM OIS WR lens" src="https://cdn.mos.cms.futurecdn.net/WBfZoDCxhpdGF4XCmvhnrj.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>Before we get to the photos, let's talk about the Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR. It's a super-telephoto APS-C lens with a 400mm focal length — that's equivalent to 609mm on a full frame sensor — and it's compatible with Fuji's X series of <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">mirrorless cameras</a>. With a maximum aperture of f/4.5, Fujifilm claims that it "delivers the highest optical performance in the XF lens lineup." That essentially means that it captures sharp photos.</p><p>As the many letters in its name suggest, the XF 400mm F4.5 R LM OIS WR features up to 5.5 stops of optical image stabilization, and it's fully protected against dust, rain and can be used in temperatures as low as 14°F / -10°C. The lens' OIS enables you to shoot at slow shutter speeds, and I can confirm that it's weather-proof as I used it in the pouring rain.</p><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="DsFU3M3iR2uAPi2Cj73Szj" name="Fujifilm_400mm_lens" alt="A Fujifilm Fujinon XF 4400mm F4.5 R LM OIS WR lens" src="https://cdn.mos.cms.futurecdn.net/DsFU3M3iR2uAPi2Cj73Szj.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 was also pleasantly surprised at how lightweight the lens felt when attached to the X-H2S. On its own, the XF 400mm F4.5 weighs 2.74lbs. With the X-H2S, the kit weighs 4.19lbs. I found the weight distribution to be fantastic, and I didn't find the kit unwieldy to use or hold up for long periods when tracking birds in flight. It certainly felt more comfortable to use than the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-review">OM System OM-1 Mark II</a> with the <a href="https://www.adorama.com/om-system-m-zuiko-digital-ed-150-600-mm-f-5-0-6-3-is-lens/p/om150600" target="_blank" rel="nofollow">M.Zuiko Digital ED 150-600mm F5.0-6.3 IS</a> (4.55lbs without camera)!</p><p>Another thing that took me by surprise? The price. The XF 400mm F4.5 has a retail price of <a href="https://www.adorama.com/fujifilm-fujinon-xf-400-mm-f-4-5-r-lm-ois-wr-lens/p/ifj40045" target="_blank" rel="nofollow">$3,199</a> / <a href="https://www.lcegroup.co.uk/New/Fujifilm-XF-400mm-F4.5-R-LM-OIS-WR-Lens_240015.html" target="_blank" rel="nofollow">£2,499</a> which is not bad for a lens of its caliber. While I wish the lens had a narrower f/2.8 aperture, it doesn't feel poor value for money. For context, the <a href="https://www.adorama.com/canon-rf-400-mm-f-2-8-l-is-usm-lens/p/car40028" target="_blank" rel="nofollow">Canon RF 400mm F2.8L IS USM</a> costs a whopping $14,000, while the <a href="https://www.adorama.com/nikon-nikkor-z-400-mm-f-4-5-vr-s-lens/p/nkz40045" target="_blank" rel="nofollow">Nikon NIKKOR Z 400mm f/4.5 VR S</a> retails for around $3,500, so similar to the Fujifilm.</p><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="WJVvSkP9Pj2C2BjANQvVbj" name="Fujifilm_400mm_lens-3" alt="A Fujifilm Fujinon XF 4400mm F4.5 R LM OIS WR lens" src="https://cdn.mos.cms.futurecdn.net/WJVvSkP9Pj2C2BjANQvVbj.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>As a wildlife photographer who's had her eye on the <a href="https://www.adorama.com/fujifilm-xf-100-400-mm-f-4-5-6-r-lm-ois-wr-lens/p/ifj100400xf" target="_blank" rel="nofollow">Fujifilm Fujinon XF 100-400mm f/4.5-5.6 R LM OIS WR</a> for a while, I'm quite tempted by the XF 400mm F4.5 R LM OIS WR. Of course, one of the XF 100-400mm's draws is that it has the versatility of a zoom lens, yet at 3.1lbs it's only ever-so-slightly heavier than the fixed-length XF 400mm.</p><p>What the XF 100-400mm doesn't have, though, is a Focus Preset memory button on its barrel, which the XF 400mm does. There's an AF-L / Preset / AF switch on XF 400mm's barrel. If you set it to Preset, aim at your target in the distance (using either the camera's autofocus or manual focusing), and press the Set button under the Preset switch, the lens memorizes the exact focus position. If you then focus on a different area and press either of the black buttons close to the front of the lens, you'll snap back to your saved distance.</p><p>I'm sure this will come in handy for wildlife and sports photographers who don't want to waste time hunting for focus when their subject enters a pre-measured zone. This also sounds useful for event photography where critical spots, like a stage, remain the same distance from the photographer.</p><h2 id="portraits-action-shots-it-does-it-all">Portraits, action shots, it does it all</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/26uZScvQzKvVsK9pZqxthW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/FppiiHYT9ispfCwVDZyxjW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2WgiHUn5qvQSWWigkGdcXW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/kD7dmi8L9bUL5Zz9mj5dfW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/tETVqpNxx2KzbMuSWbjohW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zVxW92CcTjixMAp6WWmmiW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gAigi8hFQF2CEPtiN72tiW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gRrUMs3j2Dc7iykALDzAiW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eDtyHyiK9qW8chYTNukbgW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>As part of the pre-launch event for the Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR lens, I got the chance to capture portrait and action shots of Henry the barn owl, Reggie the Harris's hawk, Hope the black-chested buzzard-eagle, Perry the peregrine falcon, and Rogue the kestrel. Very well behaved birds, I must say, as they sat still and posed for the cameras.</p><p>Have a look at the photos in the gallery above. The edges of the birds, as well as their feathers and patterns, have turned out sharp, and the background has blurred into a soft bokeh as well. Though f/4.5 isn't usually ideal for creating strong bokeh on shorter lenses, it works just fine on long telephoto lenses, like the XF 400mm. This is because the 400mm focal length naturally narrows the angle of view while heavily magnifying background elements, making distant backgrounds melt away even at a moderate f/4.5 aperture.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ZnFoSZ4YwA6AnPSxuk2ikW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/usCATEmwixKT3Vn3RBtKfV.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Capturing action shots of birds in flight is a walk in the park with the XF 400mm F4.5 R LM OIS WR, too. 400mm — and just over 600mm full frame equivalent — is plenty of reach, so you can maintain a safe distance when observing birds and other wildlife. If you want even more reach, it's worth investing in a tele-converter. The photos in the gallery below were shot without one, and I took them from afar.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/2JP4xY6rhbmxvni5vsDGiW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/frSn8XSvNZYdCq5tJNvdkW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wJc5jJx6if33dGGtgQifhW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/qR7UeiteosuWseq3dnKkhW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ik5zvXkmUuAuyAfcg9gpfW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/sPyqi9EWeMH5JPXhydEDKW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/GrHnrUZVjxAQo869YWus4W.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Ew8Zdp8XTXgHwh6rAkb4TW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/BpeNEy9J2Fs74GbXArounV.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Naturally, the quality of your action shots of birds in flight will depend on the camera body you're using. Your camera will be doing a lot of the heavy lifting with its high-speed drive, like 40fps sequential shooting on the Fuji X-H2S. For the shots above, I set the drive to 30fps (which is more than sufficient for most situations) and I chose bird subject detection, continuous tracking, and I set the shutter speed to around 1/8,000s.</p><p>As you can see, the photos have turned out sharp, thanks to the XF 400mm's optics. The glass — along with the camera — has done the beautiful birds justice. I especially love how detailed and textured the black-chested buzzard-eagle's and barn owl's wings and feathers appear.</p><p>I love how quiet the XF 400mm's focusing motors are as well. When photographing wildlife, you don't want a lens that grinds and makes loud noises that disturb your subjects (especially skittish birds). Thankfully, the XF 400mm is very quiet, and I found it quick to focus, too. Again, your camera body's autofocus system plays an important role here. The X-H2S, in conjunction with the XF 400mm, didn't let me down.</p><h2 id="looking-ahead">Looking ahead...</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="65fYpTaKGmhFdVGXCyaZXk" name="Fujifilm_400mm_lens-8" alt="A Fujifilm Fujinon XF 4400mm F4.5 R LM OIS WR lens" src="https://cdn.mos.cms.futurecdn.net/65fYpTaKGmhFdVGXCyaZXk.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've thoroughly enjoyed my time with the Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR and, of course, the Fujifilm X-H2S. I love how lightweight the lens is, making it a great choice for traveling, and its 600mm full frame equivalent focal length is more than plenty for getting close to wildlife from a distance.</p><p>You need a long telephoto lens designed for wildlife and action photography to be quiet when it's focusing and fully weather-sealed — and thankfully, the XF 400mm F4.5 R LM OIS WR is both of those things. Trust me, I tested it in the pouring rain!</p><p>For wildlife and birding photographers needing a lightweight lens, strong and pleasing bokeh, and hardcore weather resistance, the XF 400mm F4.5 R LM OIS WR sounds like a solid choice. Personally, my heart isn't <em>that </em>set on the XF 100-400mm anymore...</p><p><em>The Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR is compatible with X series cameras, and is available to pre-order for </em><a href="https://www.adorama.com/fujifilm-fujinon-xf-400-mm-f-4-5-r-lm-ois-wr-lens/p/ifj40045" target="_blank" rel="nofollow"><em>$3,199</em></a><em> / </em><a href="https://www.lcegroup.co.uk/New/Fujifilm-XF-400mm-F4.5-R-LM-OIS-WR-Lens_240015.html" target="_blank" rel="nofollow"><em>£2,499</em></a><em> (stock expected end of September).</em></p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/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/mirrorless-cameras/i-just-bought-this-5-year-old-fujifilm-camera-and-it-proves-newer-isnt-always-better">I just bought this 5-year-old Fujifilm camera and it proves newer isn’t always better</a></li><li><a href="https://www.tomsguide.com/reviews/fujifilm-xh2s">Fujifilm X-H2S review: Still worth it?</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/fujifilm-fujinon-xf-400-mm-f-4-5-lens-hands-on</link>
                                                                            <description>
                            <![CDATA[ I spent a day using the new Fujinon XF 400mm F4.5 R LM OIS WR to photograph birds of prey in flight, and it's a mighty impressive (but lightweight) lens. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ZsSqcv3wb8Um4yRoxGMAyd</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/fB3uEx8yQ75KZRmhWG8mXb-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 04 Sep 2026 05:00:00 +0000</pubDate>                                                                                                                                <updated>Fri, 04 Sep 2026 11:32:31 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/fB3uEx8yQ75KZRmhWG8mXb-1280-80.jpg">
                                                            <media:credit><![CDATA[Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A buzzard-eagle and a woman holding a Fujifilm X-H2S with a Fujinon XF 400mm F4.5 R LM OIS WR lens]]></media:description>                                                            <media:text><![CDATA[A buzzard-eagle and a woman holding a Fujifilm X-H2S with a Fujinon XF 400mm F4.5 R LM OIS WR lens]]></media:text>
                                <media:title type="plain"><![CDATA[A buzzard-eagle and a woman holding a Fujifilm X-H2S with a Fujinon XF 400mm F4.5 R LM OIS WR lens]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/fB3uEx8yQ75KZRmhWG8mXb-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>It's not very often that I get to photograph birds of prey in flight, or hold them — so when Fujifilm invited me to a pre-launch event for its new lens, I jumped at the opportunity. Today, on 4 September 2026, Fujifilm announced the new Fujinon XF 400mm F4.5 R LM OIS WR weather-sealed telephoto lens, designed for wildlife, birding and sports.</p><p>A couple of days ago, I went hands-on with it to capture portraits and action shots of falcons, eagles and owls. Though the weather was far from ideal, with the skies pouring down on us as soon as the lovely ladies from U.K.-based <a href="https://www.walkswithhawks.co.uk/" target="_blank" rel="nofollow">Walks with Hawks</a> brought out birds of prey, it was an enjoyable day out.</p><div class="product"><a data-dimension112="83a7af38-a838-11f1-8269-6f15b53251e0" data-action="Deal Block" data-label="XF 400mm F4.5 R LM OIS WR" data-dimension48="XF 400mm F4.5 R LM OIS WR" data-dimension25="$3199" href="https://www.adorama.com/fujifilm-fujinon-xf-400-mm-f-4-5-r-lm-ois-wr-lens/p/ifj40045" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1074px;"><p class="vanilla-image-block" style="padding-top:69.46%;"><img id="8Z5y44jjyTFhz8zzEGS4HJ" name="Fujinon XF 400mm" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/8Z5y44jjyTFhz8zzEGS4HJ.png" mos="" align="middle" fullscreen="" width="1074" height="746" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR is a compact, lightweight telephoto lens for wildlife, birding, and sports. Its extended focal length provides 609mm equivalent reach for wildlife, and you get up to 5.5 stops of optical image stabilization, too.<a class="view-deal button" href="https://www.adorama.com/fujifilm-fujinon-xf-400-mm-f-4-5-r-lm-ois-wr-lens/p/ifj40045" target="_blank" rel="nofollow" data-dimension112="83a7af38-a838-11f1-8269-6f15b53251e0" data-action="Deal Block" data-label="XF 400mm F4.5 R LM OIS WR" data-dimension48="XF 400mm F4.5 R LM OIS WR" data-dimension25="$3199">View Deal</a></p></div><p>I was given the 400mm lens and a <a href="https://www.tomsguide.com/reviews/fujifilm-xh2s">Fujifilm X-H2S</a> and I had a lot of fun with it. Here's everything I photographed with the kit.</p><h2 id="a-long-reaching-lightweight-tele-lens">A long-reaching, lightweight tele lens</h2><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="WBfZoDCxhpdGF4XCmvhnrj" name="Fujifilm_400mm_lens-7" alt="A Fujifilm Fujinon XF 4400mm F4.5 R LM OIS WR lens" src="https://cdn.mos.cms.futurecdn.net/WBfZoDCxhpdGF4XCmvhnrj.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>Before we get to the photos, let's talk about the Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR. It's a super-telephoto APS-C lens with a 400mm focal length — that's equivalent to 609mm on a full frame sensor — and it's compatible with Fuji's X series of <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">mirrorless cameras</a>. With a maximum aperture of f/4.5, Fujifilm claims that it "delivers the highest optical performance in the XF lens lineup." That essentially means that it captures sharp photos.</p><p>As the many letters in its name suggest, the XF 400mm F4.5 R LM OIS WR features up to 5.5 stops of optical image stabilization, and it's fully protected against dust, rain and can be used in temperatures as low as 14°F / -10°C. The lens' OIS enables you to shoot at slow shutter speeds, and I can confirm that it's weather-proof as I used it in the pouring rain.</p><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="DsFU3M3iR2uAPi2Cj73Szj" name="Fujifilm_400mm_lens" alt="A Fujifilm Fujinon XF 4400mm F4.5 R LM OIS WR lens" src="https://cdn.mos.cms.futurecdn.net/DsFU3M3iR2uAPi2Cj73Szj.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 was also pleasantly surprised at how lightweight the lens felt when attached to the X-H2S. On its own, the XF 400mm F4.5 weighs 2.74lbs. With the X-H2S, the kit weighs 4.19lbs. I found the weight distribution to be fantastic, and I didn't find the kit unwieldy to use or hold up for long periods when tracking birds in flight. It certainly felt more comfortable to use than the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-review">OM System OM-1 Mark II</a> with the <a href="https://www.adorama.com/om-system-m-zuiko-digital-ed-150-600-mm-f-5-0-6-3-is-lens/p/om150600" target="_blank" rel="nofollow">M.Zuiko Digital ED 150-600mm F5.0-6.3 IS</a> (4.55lbs without camera)!</p><p>Another thing that took me by surprise? The price. The XF 400mm F4.5 has a retail price of <a href="https://www.adorama.com/fujifilm-fujinon-xf-400-mm-f-4-5-r-lm-ois-wr-lens/p/ifj40045" target="_blank" rel="nofollow">$3,199</a> / <a href="https://www.lcegroup.co.uk/New/Fujifilm-XF-400mm-F4.5-R-LM-OIS-WR-Lens_240015.html" target="_blank" rel="nofollow">£2,499</a> which is not bad for a lens of its caliber. While I wish the lens had a narrower f/2.8 aperture, it doesn't feel poor value for money. For context, the <a href="https://www.adorama.com/canon-rf-400-mm-f-2-8-l-is-usm-lens/p/car40028" target="_blank" rel="nofollow">Canon RF 400mm F2.8L IS USM</a> costs a whopping $14,000, while the <a href="https://www.adorama.com/nikon-nikkor-z-400-mm-f-4-5-vr-s-lens/p/nkz40045" target="_blank" rel="nofollow">Nikon NIKKOR Z 400mm f/4.5 VR S</a> retails for around $3,500, so similar to the Fujifilm.</p><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="WJVvSkP9Pj2C2BjANQvVbj" name="Fujifilm_400mm_lens-3" alt="A Fujifilm Fujinon XF 4400mm F4.5 R LM OIS WR lens" src="https://cdn.mos.cms.futurecdn.net/WJVvSkP9Pj2C2BjANQvVbj.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>As a wildlife photographer who's had her eye on the <a href="https://www.adorama.com/fujifilm-xf-100-400-mm-f-4-5-6-r-lm-ois-wr-lens/p/ifj100400xf" target="_blank" rel="nofollow">Fujifilm Fujinon XF 100-400mm f/4.5-5.6 R LM OIS WR</a> for a while, I'm quite tempted by the XF 400mm F4.5 R LM OIS WR. Of course, one of the XF 100-400mm's draws is that it has the versatility of a zoom lens, yet at 3.1lbs it's only ever-so-slightly heavier than the fixed-length XF 400mm.</p><p>What the XF 100-400mm doesn't have, though, is a Focus Preset memory button on its barrel, which the XF 400mm does. There's an AF-L / Preset / AF switch on XF 400mm's barrel. If you set it to Preset, aim at your target in the distance (using either the camera's autofocus or manual focusing), and press the Set button under the Preset switch, the lens memorizes the exact focus position. If you then focus on a different area and press either of the black buttons close to the front of the lens, you'll snap back to your saved distance.</p><p>I'm sure this will come in handy for wildlife and sports photographers who don't want to waste time hunting for focus when their subject enters a pre-measured zone. This also sounds useful for event photography where critical spots, like a stage, remain the same distance from the photographer.</p><h2 id="portraits-action-shots-it-does-it-all">Portraits, action shots, it does it all</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/26uZScvQzKvVsK9pZqxthW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/FppiiHYT9ispfCwVDZyxjW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2WgiHUn5qvQSWWigkGdcXW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/kD7dmi8L9bUL5Zz9mj5dfW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/tETVqpNxx2KzbMuSWbjohW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zVxW92CcTjixMAp6WWmmiW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gAigi8hFQF2CEPtiN72tiW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gRrUMs3j2Dc7iykALDzAiW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eDtyHyiK9qW8chYTNukbgW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>As part of the pre-launch event for the Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR lens, I got the chance to capture portrait and action shots of Henry the barn owl, Reggie the Harris's hawk, Hope the black-chested buzzard-eagle, Perry the peregrine falcon, and Rogue the kestrel. Very well behaved birds, I must say, as they sat still and posed for the cameras.</p><p>Have a look at the photos in the gallery above. The edges of the birds, as well as their feathers and patterns, have turned out sharp, and the background has blurred into a soft bokeh as well. Though f/4.5 isn't usually ideal for creating strong bokeh on shorter lenses, it works just fine on long telephoto lenses, like the XF 400mm. This is because the 400mm focal length naturally narrows the angle of view while heavily magnifying background elements, making distant backgrounds melt away even at a moderate f/4.5 aperture.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ZnFoSZ4YwA6AnPSxuk2ikW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/usCATEmwixKT3Vn3RBtKfV.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Capturing action shots of birds in flight is a walk in the park with the XF 400mm F4.5 R LM OIS WR, too. 400mm — and just over 600mm full frame equivalent — is plenty of reach, so you can maintain a safe distance when observing birds and other wildlife. If you want even more reach, it's worth investing in a tele-converter. The photos in the gallery below were shot without one, and I took them from afar.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/2JP4xY6rhbmxvni5vsDGiW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/frSn8XSvNZYdCq5tJNvdkW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wJc5jJx6if33dGGtgQifhW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/qR7UeiteosuWseq3dnKkhW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ik5zvXkmUuAuyAfcg9gpfW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/sPyqi9EWeMH5JPXhydEDKW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/GrHnrUZVjxAQo869YWus4W.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Ew8Zdp8XTXgHwh6rAkb4TW.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/BpeNEy9J2Fs74GbXArounV.jpg" alt="A photo of a bird of prey captured using a Fujifilm X-H2S and XF 400mm F4.5 R LM OIS WR telephoto lens" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Naturally, the quality of your action shots of birds in flight will depend on the camera body you're using. Your camera will be doing a lot of the heavy lifting with its high-speed drive, like 40fps sequential shooting on the Fuji X-H2S. For the shots above, I set the drive to 30fps (which is more than sufficient for most situations) and I chose bird subject detection, continuous tracking, and I set the shutter speed to around 1/8,000s.</p><p>As you can see, the photos have turned out sharp, thanks to the XF 400mm's optics. The glass — along with the camera — has done the beautiful birds justice. I especially love how detailed and textured the black-chested buzzard-eagle's and barn owl's wings and feathers appear.</p><p>I love how quiet the XF 400mm's focusing motors are as well. When photographing wildlife, you don't want a lens that grinds and makes loud noises that disturb your subjects (especially skittish birds). Thankfully, the XF 400mm is very quiet, and I found it quick to focus, too. Again, your camera body's autofocus system plays an important role here. The X-H2S, in conjunction with the XF 400mm, didn't let me down.</p><h2 id="looking-ahead">Looking ahead...</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="65fYpTaKGmhFdVGXCyaZXk" name="Fujifilm_400mm_lens-8" alt="A Fujifilm Fujinon XF 4400mm F4.5 R LM OIS WR lens" src="https://cdn.mos.cms.futurecdn.net/65fYpTaKGmhFdVGXCyaZXk.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've thoroughly enjoyed my time with the Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR and, of course, the Fujifilm X-H2S. I love how lightweight the lens is, making it a great choice for traveling, and its 600mm full frame equivalent focal length is more than plenty for getting close to wildlife from a distance.</p><p>You need a long telephoto lens designed for wildlife and action photography to be quiet when it's focusing and fully weather-sealed — and thankfully, the XF 400mm F4.5 R LM OIS WR is both of those things. Trust me, I tested it in the pouring rain!</p><p>For wildlife and birding photographers needing a lightweight lens, strong and pleasing bokeh, and hardcore weather resistance, the XF 400mm F4.5 R LM OIS WR sounds like a solid choice. Personally, my heart isn't <em>that </em>set on the XF 100-400mm anymore...</p><p><em>The Fujifilm Fujinon XF 400mm F4.5 R LM OIS WR is compatible with X series cameras, and is available to pre-order for </em><a href="https://www.adorama.com/fujifilm-fujinon-xf-400-mm-f-4-5-r-lm-ois-wr-lens/p/ifj40045" target="_blank" rel="nofollow"><em>$3,199</em></a><em> / </em><a href="https://www.lcegroup.co.uk/New/Fujifilm-XF-400mm-F4.5-R-LM-OIS-WR-Lens_240015.html" target="_blank" rel="nofollow"><em>£2,499</em></a><em> (stock expected end of September).</em></p><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/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/mirrorless-cameras/i-just-bought-this-5-year-old-fujifilm-camera-and-it-proves-newer-isnt-always-better">I just bought this 5-year-old Fujifilm camera and it proves newer isn’t always better</a></li><li><a href="https://www.tomsguide.com/reviews/fujifilm-xh2s">Fujifilm X-H2S review: Still worth it?</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Busted! Catch your yard's cutest critters in action this National Wildlife Day with a trail camera ]]></title>
                                                                                                <dc:content><![CDATA[ <p>National Wildlife Day is on September 4. You can take part in the celebrations by getting a closer look at all the animals and birds in your yard with a trail camera. These camouflaged cams use motion-detection to take high quality shots of our furry and feathered friends.</p><p>Trail cameras come in a lot of different shapes and sizes, and often in packs of two so you can monitor a wider area. Some just capture stills, while others also shoot video. </p><p>For this article I've included a range of options from top brands like Bushnell and Spypoint, covering price points that start at well under $100 and up to some more high-end models. Enjoy National Wildlife Day and get acquainted with your neighborhood critters!</p>        <div class="featured_product_block featured_block_hero" data-id="cb7a3f82-a61d-11f1-868c-5386b6623d25">            <a href="https://www.amazon.com/SPYPOINT-FORCE-PRO-Low-Glow-Detection-0-2-Second/dp/B08SJ6S31N/" data-model-name="Force-Pro Trail 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/dX6VWCRsv7CHxKQnYBqgkc.jpg" alt="Spypoint Force-Pro Trail Camera 30mp Photo 4k Hd Video W/ 54 Low-Glow Leds for Image Quality Day or Night, 90’ Flash Range, 110’ Detection Range, Lcd Setup Screen | Non-Cell Cameras for Hunting"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>SPYPOINT</div>                                        <div class="featured__title">Force-Pro Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3cda-a61d-11f1-beaa-ed777bae567e">            <a href="https://www.amazon.com/GardePro-A3S-Technology-Activated-Waterproof/dp/B087G8W2PY/" data-model-name="A3S Trail 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/LsFvbMy6d5RiHR5DXuvYxa.jpg" alt="Gardepro A3s Trail Camera, 64mp Photo, 1296p Hd Video With Sound"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>GardePro</div>                                        <div class="featured__title">A3S Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3d48-a61d-11f1-9251-ab141d77eb47">            <a href="https://www.amazon.com/GardePro-E5S-Activated-Waterproof-Wildlife/dp/B0DJ572M2C/" data-model-name="E5S 2-Pack Trail 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/N8mgL2Dz2VoN6CRBugEAEb.jpg" alt="Gardepro E5s 2-Pack Trail Camera, 64mp Photo & 1296p Hd Video"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>GardePro</div>                                        <div class="featured__title">E5S 2-Pack Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3e2e-a61d-11f1-b6a3-81e42025bfa6">            <a href="https://www.amazon.com/SPYPOINT-Flex-M2-Trail-Camera-Dual-SIM/dp/B0GXZW2428/" data-model-name="Spypoint Flex-M2 Twin Pack Trail 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/ovnG5dYZh8nPCdo456X6nb.jpg" alt="Spypoint Flex-M2 Twin Pack Trail Camera, 32mp, Gps, Dual-Sim Lte"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>SPYPOINT</div>                                        <div class="featured__title">Spypoint Flex-M2 Twin Pack Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3dc0-a61d-11f1-b203-d555a4a3f952">            <a href="https://www.amazon.com/Upgraded-BlazeVideo-1920x1080P-Activated-Waterproof/dp/B0744JB986/" data-model-name="Blazevideo 2-Pack Trail 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/xyBtsy2MmszrXm55m6Q5ab.jpg" alt="Blazevideo 2-Pack Trail Camera 64mp 1296p Video, 940nm No-Glow Night Vision, 70° Focused Pir Detection (fewer False Alerts), 0.3s Trigger Motion Activated Ip66 Waterproof Game Cam for Wildlife & Yard"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BLAZEVIDEO</div>                                        <div class="featured__title">Blazevideo 2-Pack Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3bea-a61d-11f1-9840-57cb26267701">            <a href="https://www.amazon.com/Browning-Camera-Bundle-Memory-BTC6PX1080/dp/B0C4763CZZ/" data-model-name="Dark Ops Pro X 1080 Trail Game Camera Bundle" 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/mWcYSM4yt8sxN2PXcD4aba.jpg" alt="Browning Dark Ops Pro X 1080 Trail Game Camera Bundle Includes 32gb Memory Card and J-Tech Card Reader (24mp) | Btc6px1080"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Browning</div>                                        <div class="featured__title">Dark Ops Pro X 1080 Trail Game Camera Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3c62-a61d-11f1-98bb-dd659185a703">            <a href="https://www.amazon.com/Browning-Trail-Camera-Defender-Universal/dp/B0GCL5KJJH/" data-model-name="Trail Camera Bundle" 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/8W5aTJ4AENfDyy4R6h5Tna.jpg" alt="Browning Trail Camera Bundle"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Browning</div>                                        <div class="featured__title">Trail Camera Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3f14-a61d-11f1-bb4f-9164701c51bb">            <a href="https://www.amazon.com/Stealth-Cam-Bundle-No-Glow-100-Ft/dp/B07X35RMZZ/" data-model-name="Stealth Cam 2022 G42NG 32MP Trail 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/8fcBFPuhN4bXgLwHJKSE48.jpg" alt="Stealth Cam, Stealth Cam 2022 G42NG 32MP Trail Camera No-Glow Flash (Kryptek Camouflage) | 2-Pack, 100-Ft, (2 items)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Stealth Cam</div>                                        <div class="featured__title">Stealth Cam 2022 G42NG 32MP Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3e9c-a61d-11f1-a177-154dd80c596b">            <a href="https://www.amazon.com/Bushnell-Camera-Hunting-Camera-Images/dp/B09DHL4S6J/" data-model-name="Core DS-4K No Glow Trail 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/wozkwdvqiJv7JEMfDyS3Dc.jpg" alt="Bushnell Core Ds-4k No Glow Trail Camera"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Bushnell</div>                                        <div class="featured__title">Core DS-4K No Glow Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/busted-catch-your-yards-cutest-critters-in-action-this-national-wildlife-day-with-a-trail-camera</link>
                                                                            <description>
                            <![CDATA[ Trail cameras are great if you have a yard or garden and want to keep an eye on all the animals and birds that are paying you a visit. Why not pick one up and celebrate National Wildlife Day on September 4 in style. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">BnFvihKqBhdByQgwaykeDe</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/XwNNVL8zrmXj3hktwiGJQK-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 01 Sep 2026 18:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras & 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/XwNNVL8zrmXj3hktwiGJQK-1280-80.jpg">
                                                            <media:credit><![CDATA[Blazevideo, Bushnell, Spypoint / Edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of trail cameras in some woods.]]></media:description>                                                            <media:text><![CDATA[A selection of trail cameras in some woods.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of trail cameras in some woods.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/XwNNVL8zrmXj3hktwiGJQK-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>National Wildlife Day is on September 4. You can take part in the celebrations by getting a closer look at all the animals and birds in your yard with a trail camera. These camouflaged cams use motion-detection to take high quality shots of our furry and feathered friends.</p><p>Trail cameras come in a lot of different shapes and sizes, and often in packs of two so you can monitor a wider area. Some just capture stills, while others also shoot video. </p><p>For this article I've included a range of options from top brands like Bushnell and Spypoint, covering price points that start at well under $100 and up to some more high-end models. Enjoy National Wildlife Day and get acquainted with your neighborhood critters!</p>        <div class="featured_product_block featured_block_hero" data-id="cb7a3f82-a61d-11f1-868c-5386b6623d25">            <a href="https://www.amazon.com/SPYPOINT-FORCE-PRO-Low-Glow-Detection-0-2-Second/dp/B08SJ6S31N/" data-model-name="Force-Pro Trail 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/dX6VWCRsv7CHxKQnYBqgkc.jpg" alt="Spypoint Force-Pro Trail Camera 30mp Photo 4k Hd Video W/ 54 Low-Glow Leds for Image Quality Day or Night, 90’ Flash Range, 110’ Detection Range, Lcd Setup Screen | Non-Cell Cameras for Hunting"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>SPYPOINT</div>                                        <div class="featured__title">Force-Pro Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3cda-a61d-11f1-beaa-ed777bae567e">            <a href="https://www.amazon.com/GardePro-A3S-Technology-Activated-Waterproof/dp/B087G8W2PY/" data-model-name="A3S Trail 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/LsFvbMy6d5RiHR5DXuvYxa.jpg" alt="Gardepro A3s Trail Camera, 64mp Photo, 1296p Hd Video With Sound"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>GardePro</div>                                        <div class="featured__title">A3S Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3d48-a61d-11f1-9251-ab141d77eb47">            <a href="https://www.amazon.com/GardePro-E5S-Activated-Waterproof-Wildlife/dp/B0DJ572M2C/" data-model-name="E5S 2-Pack Trail 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/N8mgL2Dz2VoN6CRBugEAEb.jpg" alt="Gardepro E5s 2-Pack Trail Camera, 64mp Photo & 1296p Hd Video"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>GardePro</div>                                        <div class="featured__title">E5S 2-Pack Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3e2e-a61d-11f1-b6a3-81e42025bfa6">            <a href="https://www.amazon.com/SPYPOINT-Flex-M2-Trail-Camera-Dual-SIM/dp/B0GXZW2428/" data-model-name="Spypoint Flex-M2 Twin Pack Trail 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/ovnG5dYZh8nPCdo456X6nb.jpg" alt="Spypoint Flex-M2 Twin Pack Trail Camera, 32mp, Gps, Dual-Sim Lte"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>SPYPOINT</div>                                        <div class="featured__title">Spypoint Flex-M2 Twin Pack Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3dc0-a61d-11f1-b203-d555a4a3f952">            <a href="https://www.amazon.com/Upgraded-BlazeVideo-1920x1080P-Activated-Waterproof/dp/B0744JB986/" data-model-name="Blazevideo 2-Pack Trail 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/xyBtsy2MmszrXm55m6Q5ab.jpg" alt="Blazevideo 2-Pack Trail Camera 64mp 1296p Video, 940nm No-Glow Night Vision, 70° Focused Pir Detection (fewer False Alerts), 0.3s Trigger Motion Activated Ip66 Waterproof Game Cam for Wildlife & Yard"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>BLAZEVIDEO</div>                                        <div class="featured__title">Blazevideo 2-Pack Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3bea-a61d-11f1-9840-57cb26267701">            <a href="https://www.amazon.com/Browning-Camera-Bundle-Memory-BTC6PX1080/dp/B0C4763CZZ/" data-model-name="Dark Ops Pro X 1080 Trail Game Camera Bundle" 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/mWcYSM4yt8sxN2PXcD4aba.jpg" alt="Browning Dark Ops Pro X 1080 Trail Game Camera Bundle Includes 32gb Memory Card and J-Tech Card Reader (24mp) | Btc6px1080"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Browning</div>                                        <div class="featured__title">Dark Ops Pro X 1080 Trail Game Camera Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3c62-a61d-11f1-98bb-dd659185a703">            <a href="https://www.amazon.com/Browning-Trail-Camera-Defender-Universal/dp/B0GCL5KJJH/" data-model-name="Trail Camera Bundle" 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/8W5aTJ4AENfDyy4R6h5Tna.jpg" alt="Browning Trail Camera Bundle"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Browning</div>                                        <div class="featured__title">Trail Camera Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3f14-a61d-11f1-bb4f-9164701c51bb">            <a href="https://www.amazon.com/Stealth-Cam-Bundle-No-Glow-100-Ft/dp/B07X35RMZZ/" data-model-name="Stealth Cam 2022 G42NG 32MP Trail 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/8fcBFPuhN4bXgLwHJKSE48.jpg" alt="Stealth Cam, Stealth Cam 2022 G42NG 32MP Trail Camera No-Glow Flash (Kryptek Camouflage) | 2-Pack, 100-Ft, (2 items)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Stealth Cam</div>                                        <div class="featured__title">Stealth Cam 2022 G42NG 32MP Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="cb7a3e9c-a61d-11f1-a177-154dd80c596b">            <a href="https://www.amazon.com/Bushnell-Camera-Hunting-Camera-Images/dp/B09DHL4S6J/" data-model-name="Core DS-4K No Glow Trail 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/wozkwdvqiJv7JEMfDyS3Dc.jpg" alt="Bushnell Core Ds-4k No Glow Trail Camera"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Bushnell</div>                                        <div class="featured__title">Core DS-4K No Glow Trail Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve taken over 3,000 photos on the Sony RX10 V — its 25x zoom lens and 30fps blackout-free burst make it the king of bridge cameras ]]></title>
                                                                                                <dc:content><![CDATA[ <p>The king of bridge cameras returns. Nearly a decade since the fourth-gen camera came out, Sony has introduced substantial improvements and upgrades to make the RX10 V a simply outstanding camera. With better ergonomics and controls, and bigger battery life, the RX10 V feels more like a mirrorless Alpha camera — and I’m not complaining.</p><p>Handling the RX10 V is a joy, and its long-reaching 24-600mm (full frame equivalent) lens lets you get up, close and personal with your subjects. Thanks to 30fps blackout-free drive and excellent autofocus, this is a solid camera for amateur wildlife and sports photographers. The 20.1MP images boast beautiful color reproduction, too.</p><p>Though not without its flaws, there aren’t many reasons to not buy this camera. To find out if this is the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best camera </a>for you, read my full Sony RX10 V review.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-specs"><span>Sony RX10 V review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.adorama.com/sony-dsc-rx10-v-digital-camera/p/isorx10m5" target="_blank" rel="nofollow">$2,298</a> / <a href="https://www.lcegroup.co.uk/New/Sony-RX10-V-Digital-Camera_234898.html" target="_blank" rel="nofollow">£2,199</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>20.1MP 1-inch Stacked Exmor RS CMOS</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>BIONZ XR</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Optical SteadyShot</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Fast Hybrid AF (phase-detection AF + contrast-detection AF)</p></td></tr><tr><td class="firstcol " ><p><strong>Focus modes</strong></p></td><td  ><p>Single Shot, Continuous AF, DMF, Manual focus</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>0.5-inch Quad-VGA OLED EVF, approx. 3.68 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3-inch TFT LCD touchscreen, approx. 1.6 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>ISO range</strong></p></td><td  ><p>64-12,800</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>4K/60fps | 4K/120fps | FHD/60fps</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>USB-C, micro HDMI, USB Micro-B 2.0, 3.5mm mic in, 3.5mm headphones out, 1x SD/SDHC/SDXC UHS-I/II</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless/bluetooth</strong></p></td><td  ><p>Wi-Fi, Bluetooth</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>30fps (electronic) | 10fps (mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Max shutter speed</strong></p></td><td  ><p>30s to 1/16,000s (stills, electronic) | 30s to 1/2,000s (stills, mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>630 shots (LCD) | 570 shots (EVF)</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>5.37 x 3.72 x 5.96 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.7lbs (with battery and card)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-rx10-v-review-price-availability"><span>Sony RX10 V review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D7njNQMomC9joTYj4eczBd" name="Sony_RX10_ 1.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/D7njNQMomC9joTYj4eczBd.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V has been nearly a decade in the making, and it succeeds the RX10 IV which first came out in 2017. The RX10 V is a bridge camera that literally bridges the gap between basic point-and-shoot compacts and full frame cameras, and it borrows features from Sony’s Alpha lineup (more on that shortly). It retails for <a href="https://www.adorama.com/sony-dsc-rx10-v-digital-camera/p/isorx10m5" target="_blank" rel="nofollow">$2,298 at Adorama U.S.</a> / <a href="https://www.lcegroup.co.uk/New/Sony-RX10-V-Digital-Camera_234898.html" target="_blank" rel="nofollow">£2,199 at London Camera Exchange U.K.</a></p><p>It’s pricier than the <a href="https://www.tomsguide.com/cameras-photography/nikon-coolpix-p1100-review">Nikon Coolpix P1100</a> ($1,146) and the Panasonic Lumix DMC-FZ2000/FZ2500 ($1,049) but it’s worth the extra spend if you want 30fps blackout-free burst shooting, fantastic autofocus, 4K/60fps video, and long battery life.</p><p>Spending nearly $3,000 on a camera may sound like a lot. But given that the camera features a built-in 24-600mm (full frame equivalent) lens, it negates the need to drop nearly a grand or more on a lens. This softens the blows a bit — especially because a telephoto lens on its own can set you back serious money. For context, a Sony 400-800mm lens costs over $3,000, while Canon’s RF1200mm lens costs well over $22,000.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-design"><span>Sony RX10 V review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D5jR7azS4uEABT7YnzZQ9d" name="Sony_RX10_ 3.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/D5jR7azS4uEABT7YnzZQ9d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Bridge cameras offer the convenience of point-and-shoot compacts and stuff it into DSLR-esque bodies. It’s no surprise, then, that the Sony RX10 V is slightly bulky, measuring 5.37 x 3.72 x 5.96 inches and weighing 2.7lbs (with battery and cards). It’s certainly bigger than my trusty mirrorless <a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-t50-review">Fujifilm X-T50</a> ($1,399) but even so, it’s more compact and lighter than the Nikon Coolpix P1100.</p><p>Unlike the Nikon Coolpix P1100, the RX10 V doesn’t feel as unwieldy to handle, thanks to its spacious protruding grip, which is deep and provides plenty of space for your fingers to wrap around it. Compared to the RX10 IV, the fifth-gen model offers better ergonomics, from its deeper grip to its more prominent command dials. The RX10 V feels like a modern Alpha camera, like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-spent-a-day-shooting-on-the-new-sony-a7-v-here-are-my-first-impressions-spoiler-alert-its-great">Sony A7 V</a> ($2,899). Build quality feels solid, too, and I’d expect nothing less from Sony. </p><h2 id="viewfinder">Viewfinder</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:879px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="Tk6ZE7ercUexT3wNHJ2X7d" name="Sony_RX10_ 7.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/v2/t:43,l:716,cw:879,ch:494,q:80/Tk6ZE7ercUexT3wNHJ2X7d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The RX10 V’s viewfinder has been upgraded. While its predecessor featured a 0.39-inch XGA OLED electronic viewfinder (EVF) with 0.7x magnification, the RX10 V packs a 0.5-inch Quad-VGA OLED EVF with 0.78x magnification — similar to the one on the flagship <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II </a>($6,499).</p><p>The resolution has received a bump, too, from 2.35 million dots on the RX10 IV to 3.68 million dots on the RX10 V. You now get a sharp, detailed, life-like representation of the scene you’re capturing. This helps you accurately frame and capture your subject. The RX10 V’s EVF resolution is similar to the Sony A7 V’s (3.69M dots), the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">Canon EOS R6 Mark III</a>’s (3.69M dots, $2,799) and the <a href="https://www.tomsguide.com/reviews/panasonic-lumix-g9-ii">Panasonic Lumix G9II</a>’s (3.68M dots, $1,899), though it’s slightly less than the Nikon Coolpix P1100’s (2.36M dots).</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:7365px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kJeKaFvJfxFCK8di2AFVra" name="Sony_RX10V" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/kJeKaFvJfxFCK8di2AFVra.jpg" mos="" align="middle" fullscreen="" width="7365" height="4143" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>I’ve found the RX10 V’s viewfinder extremely comfortable to look through, even through my prescription glasses. I also love that its refresh rate can be set to either 60Hz or 120Hz — the same as on the Canon EOS R6 Mark III and the much pricier <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r5-mark-ii-review">Canon EOS R5 Mark II</a> ($4,299). 120Hz refresh rate ensures that the subjects you’re tracking remain free of motion blur, and the RX10 V delivers smooth and realistic motion, making it great for fast-paced sports and wildlife photography.</p><p>The RX10 V borrows another feature from the Sony A7 V, and that’s 30fps blackout-free burst shooting. This means that there are no black screens between shots, enabling you to keep a constant view of your subject as you shoot. This is something we’ve also seen on other wildlife-oriented cameras, like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-review">OM System OM-1 Mark II</a> ($2,399), and the RX10 V wins brownie points over the Canon EOS R6 Mark III here which doesn’t feature blackout-free sequential shooting.</p><h2 id="monitor-2">Monitor</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Tk6ZE7ercUexT3wNHJ2X7d" name="Sony_RX10_ 7.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/Tk6ZE7ercUexT3wNHJ2X7d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The RX10 V’s rear monitor has received a modest upgrade, too. While the screen size remains the same — 3-inch on both the RX10 IV and the RX10 V — the touchscreen now has a resolution of 1.6 million dots. The 3:2 aspect ratio also matches the camera’s sensor now, as opposed to the 4:3 aspect ratio on the RX10 V’s predecessor.</p><p>I’ve found the RX10 V’s monitor highly responsive in my testing, and I’ve had no trouble viewing it in different lighting conditions, including direct sunlight. It’s also much brighter than the Nikon Coolpix P1100’s 921K-dot monitor. The RX10 V’s screen can tilt up and down but it can’t hinge or tilt sideways, like the Nikon Coolpix P1100’s can, though, which limits the extent of shooting at awkward, low-to-the-ground or overhead angles.</p><h2 id="ports-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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="abHg4KmiWHT4D5i2uHMp6d" name="Sony_RX10_ 4.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/abHg4KmiWHT4D5i2uHMp6d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>All the ports are located on the RX10 V’s left edge and are protected by plastic flaps. You get a USB-C port for charging and data transfer; a legacy USB Micro-B port for accessories; a micro HDMI port; and dual 3.5mm ports for mic in and headphones out. The memory card slot is located next to the battery pack on the bottom plate.The RX10 V is compatible with a single SD/SDHC/SDXC UHS-I/II card.</p><h2 id="weather-sealing-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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7ytCwpXxA3Gc7jmJpscjCd" name="Sony_RX10_ 5.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/7ytCwpXxA3Gc7jmJpscjCd.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Unlike the Nikon Coolpix P1100, the RX10 V is fully weather-sealed and protected against moisture and dust. This gives you more confidence when shooting outdoors and in changeable weather conditions.</p><p>The RX10 V’s weather-sealing comes at a cost, though — but not a huge one. There’s no built-in flash, like on previous iterations of the line, but a small pop-up flash is rarely powerful enough to light up distant subjects (perhaps macro subjects, yes), so it’s not something you’ll necessarily miss.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-controls"><span>Sony RX10 V review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9RS5FmpcSe7aHkAiAmGy4d" name="Sony_RX10_ 11.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/9RS5FmpcSe7aHkAiAmGy4d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V is a delight to use, thanks to its well-placed and large buttons and dials. On the RX10 IV, an LCD screen took up a fair bit of space on the top plate, but this has been replaced by a prominent command dial that lets you swap between shooting modes as well as stills, video, and slow-and-quick (S&Q) video. You now get additional dials for shutter speed and ISO, too.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="wFs93caYnHmcFtxuY3Ud9d" name="Sony_RX10_ 13.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/wFs93caYnHmcFtxuY3Ud9d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I like that the dual custom (C1 and C2) buttons have been moved up and are now located next to the shutter button. This makes accessing them much easier, and it makes shooting feel more intuitive. Sony has also added a physical focus joystick, similar to the one on its latest Alpha models, on the rear panel — something that was absent on the RX10 IV. The navigation wheel is bigger, too, and again, feels like it’s been borrowed from the A7 V or the A1 II.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5C4qbvPkai7F8T7xRJzg6d" name="Sony_RX10_ 10.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/5C4qbvPkai7F8T7xRJzg6d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>These quality of life improvements result not only in better ergonomics, but also in a more well-rounded user experience. My only complaint is that the RX10 V’s three dials are only accessible by your right thumb, and I would have much preferred having a front dial for adjusting, say, shutter speed. Maybe sixth time’s the charm, eh, Sony?</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="35J5e8aib7AhbiUV38Yy6d" name="Sony_RX10_ 9.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/35J5e8aib7AhbiUV38Yy6d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>A decade is a long time, and it isn’t surprising that the RX10 IV’s menu system has been overhauled to deliver a more user-friendly experience on the RX 10 V. Instead of being text-heavy, the RX10 V’s menu system is color-coded, feels more logical and less cluttered, and it’s clearly been modeled after recent Alpha cameras.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-autofocus"><span>Sony RX10 V review: Autofocus</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nkcaZDv8EKT9uRjAoukS8d" name="Sony_RX10_ 6.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/nkcaZDv8EKT9uRjAoukS8d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V features an advanced hybrid autofocus system which uses a combination of phase- and contrast-detection AF, driven by the BIONZ XR processor. It inherits the Sony A7 V’s AI chip with real-time subject detection autofocus, and the Sony A1 II’s Auto Subject Detection which automatically swaps between human, bird/animal, insect, and vehicle detection modes.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/JFE9mn8hPpRNXpxfzF8wca.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iRW75EF84nSRu3G2P2wW4d.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fVjajBpMmn5KoRLSdfW2mc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/8fmPW9UNj8EPUqrfaZYmBd.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/bFDXmBF9nrRLzKcQj3rTKa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4CpeZt9uE28bQJxuwVeoJb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Regardless of the subject I was photographing, the RX10 V didn’t let me down. As you can see in the gallery above, the camera was quick to find and lock on to birds, humans, and insects. When I was photographing birds and insects at a lake, I appreciated the camera’s Auto Subject Detection, and I found it very reliable. I was able to get the shots I needed as I didn’t have to waste time swapping between subjects manually.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/LXnxDGR9jCmMCNgcBzuaTc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/rr3xKP6dgojmVm3NVCwnaZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The photos above aren’t my finest work, but I wanted to use these two photos to demonstrate how well the RX10 V’s autofocus works. Both the squirrel and bird were obscured by leaves, and because of very bright sunlight, I couldn’t focus on them with my own eyes — so I pointed my camera right overhead and the RX10 V immediately locked on to them. <em>That’s </em>how smart the camera’s AF system is.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/4ptzqxEicPqDN7Fx5Tgvec.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/uAEw5jkaU6BDFKBxondJAd.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/euKhgZ5EbrzNd6JcUwPLcc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9bZFMauzQ4t7LDrmLo8LYc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I also appreciated the RX10 V locking on to and tracking the black-headed gull as I fired off a burst. The autofocus didn’t break at all, even as the gull landed on the water, surrounded by other birds. As you can see in the gallery above, it’s still in focus in the last two shots. </p><h2 class="article-body__section" id="section-sony-rx10-v-review-stabilization"><span>Sony RX10 V review: Stabilization</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/iR74apVdMuhtB9pboSqCyX.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 1/3s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MLd3MR7SrVTM6ktQgxZwUX.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 1/2s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V utilizes Sony’s SteadyShot optical image stabilization which is also found in the <a href="https://www.tomsguide.com/cameras-photography/sony-zv-e10-ii-review">Sony ZV-E10 II</a> ($999) and the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-rx1r-iii-review">Sony RX1R III</a> ($5,099), and it incurs a crop. The lack of in-body image stabilization (IBIS) means you can’t shoot at super slow shutter speeds, but it should just about suffice when shooting handheld.</p><p>In my testing, I was able to shoot handheld at shutter speeds as slow as 1/3s (first image above) but any slower than that, like 1/2s (second image above), and the resulting image was blurry, impacted by camera shake. This is still good, though!</p><h2 class="article-body__section" id="section-sony-rx10-v-review-image-quality"><span>Sony RX10 V review: Image quality</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4867px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="9g4U23oDsUQ9m8i9Fn36ca" name="Sony_RX10V-2" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/9g4U23oDsUQ9m8i9Fn36ca.jpg" mos="" align="middle" fullscreen="" width="4867" height="2738" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V packs a small 1-inch Stacked Exmor RS CMOS sensor with a 20.1MP resolution. Though small, its sensor is still bigger than the Nikon Coolpix P1100’s 1/2.3-inch 16MP one. You can’t make extreme crops as you can on, say, the 33MP full frame Sony A7 V, but given that you get a 24-600mm effective focal length, you can still capture detailed images of distant subjects.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/aKy3htXcWfzsPYo6ayoiic.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/NjLg2nEStFVDRLaPk4cVoc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 24mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Before we dive into image quality, let’s talk about the RX10 V’s reach. The RX10 V is fitted with a ZEISS Vario-Sonnar T 24-600mm lens, and this full frame equivalent focal length is achieved through its 25x optical zoom. In the gallery above, the first shot was taken at 600mm while the second at 24mm, and both images pack a lot of detail.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/koPJ2V2hcfjuC2mb4eMafa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken with 25x optical zoom + 4x digital zoom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/H2oqnmXrnH5vbjMMnuCJ7b.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QqfrYzXTy2HhQnT7dKXsVc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 24mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>In addition to the optical zoom, you also get a 4x digital zoom. Once the camera has reached 600mm, it applies a 4x digital zoom and starts cropping to achieve 1200mm. Naturally, this detrimentally impacts image quality as you're cropping into an already modest 20.1MP and losing resolution. The first image of the cormorants above was taken with the 4x digital zoom, while the second was taken with the 25x optical zoom, and the third is the fully zoomed out original. As you can see in the first slide, there is a drop in resolution and detail. I’d recommend sticking with the 25x optical zoom to get the best images.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/hVaTwpPRC58sNv4UdUt5YY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Extreme crop of second image.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QNfbtSaVopXsfE34GFmtHZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Original image, taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Cropping after the fact in post-production yields similar results. The first shot in the gallery above is an extreme crop of the second photo, and once again, there's a drop in the quality. If you want a camera that allows for extreme cropping, you’ll have to splurge on the likes of the Sony A7 V or the Canon EOS R6 Mark III.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/9eVdVnGos5dnbD5nSWMSfZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mPgb9TWudejtMEjkwLVEZY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fTuAXvnhsCsAikTfzms9oa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gFzAupsVop5uB2PeKAoQiZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/i94rhfK6zQpet95rQUSE3b.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YfTRcRwp2Wa5p4EugGf3iZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wGVUM6KS5dse9vjBuUKjXZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/my5mooKexmgcqDcW2hq4dZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/uNfNRb8dsqJgef9dkX8LyY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pY2PMsAoGF7JgVGduFCpVZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6VuMVLqxwaMUrPntUoeMPb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/32Fyehx9h5z3vFTv6UKT8d.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Kacb443xQb6QGoHTxPHMvc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Fwgdx5JmnHRsmxQV5QEWc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/vDh3WvT6h8qixbhFsLhQ6a.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Now that that’s out of the way, let’s finally talk about image quality — you can see sample shots in the gallery above. Color reproduction is stunning and accurate. The different shades and hues look beautiful, and I like that none of them look oversaturated or high on contrast. Even the neon-green algae looks vibrant but not overly bright.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ufk6b4Eq5dSSbpfT35YNba.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zjgeDkaj2jD6ELkPwc8zra.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EwTnnbN5dUausmF9cgmbpZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/DoAfnyVgzFH6dJzpanRJZa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V also packs 12 of Sony’s Creative Looks, which are in-camera filters or presets, similar to those you’ll find on Canon or Fujifilm cameras. These include but aren’t limited to Portrait, Film, Soft Highkey, and others. The images of the swans above were shot using the Black & White Creative Look, and I think they look gorgeous. I love how dramatic it makes the swans look, and the camera has also done a good job of capturing the fine details, such as the water droplets on the first swan’s head, and the tiny feathers on the last bird’s beak.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/jJnCanpf3nutuMBAq5yufa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/28itQ3AW9sY4UBSSMa2NQa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>For the above photos of the kingfisher, I opted for the Vivid Creative Look which boosts contrast, saturation and vibrancy. The resulting images appear punchy, fitting for such a colorful bird. You can also customize each Creative Look’s parameters, like the contrast, highlights, and shadows, giving you more flexibility.</p><h2 id="high-speed-drive">High-speed drive</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/M2vnTC2oXbcHPPubN2bLhb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/5VNJmWkpUvGhKtPZDcf3ib.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Lr6rWPSbxvBFAGm6HqoWZb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>30fps blackout-free sequential shooting via its electronic shutter is one of the RX10 V’s headline specs, and it’s seriously impressive. It can capture 546 JPEGs, 141 RAW and JPEGs, or 162 lossless RAW files when using the 10fps mechanical shutter. Having 30fps drive at your disposal means you can capture every single moment of action, making the RX10 V a solid choice for wildlife and sports. In comparison, the Nikon Coolpix P1100 has a measly 7fps drive.</p><p>The electronic shutter has a speed of 30s to 1/16,000s, and I bumped the speed up to 1/8,000s to capture the sequence of the coot flying towards the other. The mechanical shutter, on the other hand, tops out at 1/2,000s, so I’d recommend using the electronic shutter when you can to get blur-free images.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Qsd46T7eYFxnK2rYRGYKZZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eu6m2YBPRWp52KV34SUqZZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YpQ2Mfqh9rbdpzBaTsKJhZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nBTC6uqXS53oG7B4dyLUbZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2NcCD9RJeit4JjLJMr5oZZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ynqbZjmrxAWDdqb3guEQcZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/PsKoCGXt5o9CgddYoD3yfZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hxsdU6ZqVBAkowhXbo6qhZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Q3KCzpCH2f3kpjSr27MfZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jKtASKCJRdeKGdzrVszSgZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mt37knVkRTGxGrtjDt2vdZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Kd4Kbz8xTX6kqVViYuk2dZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Thanks to 30fps blackout-free burst and incredible autofocus that didn’t lose track of the tiny bird even once, I was able to capture the images (above) of the kingfisher taking off. The only downside to the RX10 V’s high-speed drive is that the camera doesn’t feature pre-burst capture, so it doesn’t start recording the frames when the shutter button is half-pressed. Again, you’ll need to get the Sony A7 V, the Canon EOS R6 Mark III or the OM System OM-1 Mark II if you want that.</p><h2 id="iso-performance">ISO performance</h2><p>The RX10 V doesn’t have as massive an ISO range as the one offered by the Sony A7 V — but it’s double the Nikon Coolpix P1100’s. The RX10 V has a base ISO of 64 and it tops out at 12,800. Given the camera’s small 1-inch sensor, high ISO performance isn’t anything to write home about.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/GmLNrK7Ta3aUWZGz8SYoLZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO8,000. JPEG export of ARW file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zErphYTPJERbUj8gu8UCWY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO8,000. SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The results were as expected at ISO6,400, so I bumped the ISO up to 8,000. The first image in the gallery above is a JPEG export of the ARW file, while the second is the straight-out-of-camera (SOOC) JPEG. While the in-camera noise reduction algorithms have done well to reduce the grain in the SOOC JPEG, the image has lost some sharpness around the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/8bitdo-retro-cube-2-review">8BitDo Retro Cube 2</a>’s corners and edges, and the blue photobook looks too soft. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/6G2rgFCEavpmQUytTyEBWZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO12,800. JPEG export of ARW file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ktPix7GYjNzQrTnQJVwKiY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO12,800. SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I then shot at ISO12,800. The first image (JPEG export of ARW file) has been ruined by noise, and the second shot (SOOC JPEG) isn’t that much better. You can see a lot of grain in the photobook as well as the speaker. The detail in the speaker’s grille has been lost, and the image appears flat. I wouldn’t recommend shooting at such high ISO unless and until it’s your last resort.</p><p>This is part and parcel of having a small sensor, but the RX10 V still performs better than the Nikon Coolpix P1100, whose 1/2.3-inch sensor produces extremely noisy images at just ISO6,400.</p><h2 id="dynamic-range">Dynamic range</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/QWHE5jxbL5cCqTTGCcJP9a.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO level 6, unedited.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/DqMaRZzFKdCuR9wyYyfwDa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO level 6, shadows boosted and highlights reduced in Adobe Photoshop Lightroom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V offers up to 9+ stops of dynamic range, and it features a D-Range Optimizer (DRO) mode which offers eight varying levels of balancing bright highlights and dark shadows.</p><p>The two images in the gallery above are the same shot, taken at DRO-6. The first image is the unedited RAW file. I boosted the shadows and reduced the highlights, which resulted in the second shot. Boosting the shadows has revealed a surprising amount of detail in the dark areas, such as the bushes in the distance. The shadows are fairly noise-free too, which is impressive.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/hxDyJjMNf3DUKCwNxSPvgb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO disabled, unedited.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZsssQKvQzWhFs8ngoa5WyY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO disabled, shadows boosted and highlights reduced in Adobe Photoshop Lightroom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The backlit photo of the kingfisher above, on the other hand, was shot with DRO disabled. I edited the shot in Adobe Photoshop Lightroom and as you can see in the second slide, the camera has done a decent job of maintaining highlight detail while exposing for the bird’s comparatively dim unlit body. There’s some noticeable noise in the image, though, especially in the background (have a look at the bottom-left corner).</p><h2 class="article-body__section" id="section-sony-rx10-v-review-video"><span>Sony RX10 V review: Video</span></h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="high" data-lazy-src="https://www.youtube-nocookie.com/embed/FIVteoDdo2k" allowfullscreen></iframe></div></div><p>The Sony RX10 V is a competent hybrid camera, capable of recording 4K or FHD video at 60fps, and slow-mo 4K video at 120fps — the Nikon Coolpix P1100 can shoot 4K video at 30fps only. You can see a compilation of clips filmed at 4K/60fps above. I think the video looks great. Movement is fluid and smooth, while all the subjects remain in focus. The clips are fairly stable and not <em>too </em>shaky given that I was shooting handheld.</p><p>Similar to Sony’s vlogging cameras, you also get S-Log3 which captures a wide dynamic range, making it easier to color grade in post-production. If you want straight-out-of-camera slow-mo video, the camera’s slow-and-quick (S&Q) mode captures already slowed video (without any audio) which can be shared instantly without any need for editing in post.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-battery-life"><span>Sony RX10 V review: Battery life</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5775px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="oUEaneDtJ5gCu34n37Vjga" name="Sony_RX10V-4" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/oUEaneDtJ5gCu34n37Vjga.jpg" mos="" align="middle" fullscreen="" width="5775" height="3248" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>Unlike the NP-FW50 battery pack used by the Sony RX10 IV, the Sony RX10 V utilizes the NP-FZ100 battery pack. It’s the same battery used by the likes of the Sony A1 II and the Sony A7 V. Thanks to the NP-FZ100 battery, the RX10 V now lasts longer than the RX10 IV. It’s CIPA-rated for 630 shots (versus 400) when using the monitor or 570 shots (versus 370) with the EVF.</p><p>CIPA testing is conducted under strict conditions and the ratings are usually conservative. More often than not, your camera will last you longer in the field. In my testing, I was able to get over 3,000 shots, which included a mix of burst sequences and RAW images, and I walked away with just under 50% battery. Take this with a pinch of salt, though, as your mileage will vary depending on your settings and shooting conditions.</p><p>The RX10 V lasts much longer than the Nikon Coolpix P1100 which is CIPA-rated for just 260 shots, and the Panasonic Lumix DMC-FZ2000/FZ2500 which yields 270 shots on a single charge. The RX10 V also upstages mirrorless beasts, like the Sony A1 II (520 shots) and the Canon EOS R6 Mark III (620 shots).</p><h2 class="article-body__section" id="section-sony-rx10-v-review-verdict"><span>Sony RX10 V review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="cJ9soApjYBhfmusZYHAHzc" name="Sony_RX10_.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/cJ9soApjYBhfmusZYHAHzc.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want a powerful bridge camera that feels like a mirrorless camera, I can’t recommend the Sony RX10 V enough. It packs a long-reaching lens with a 25x optical zoom, letting you get close-up shots of far-away birds. The RX10 V feels sublime to use and handle, thanks to improved ergonomics and controls. It’s got long battery life to boot, too.</p><p>What makes the RX10 V worth the premium is its fantastic and accurate autofocus, as well as 30fps blackout-free high-speed drive. Combined, both of these features make the RX10 V a formidable camera for sports and wildlife photography — or videography, as the camera is capable of recording 4K/60fps video.</p><p>While the camera doesn’t feature the best high ISO performance, given its small 1-inch sensor that can’t take on full frame beasts, this small complaint is overshadowed by everything else on offer. The RX10 V blurs the line between bridge and Sony’s Alpha lineup to deliver one of the best cameras of 2026.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/sony-rx10-v-review</link>
                                                                            <description>
                            <![CDATA[ The Sony RX10 V improves on its predecessor with better ergonomics, controls, longer battery life, 30fps blackout-free shooting, and incredible autofocus. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">56dffiHvhBWcRuCY7zNLWb</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/fMM6N28iCaLdXrusTQta3a-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 27 Aug 2026 13:11:26 +0000</pubDate>                                                                                                                                <updated>Thu, 27 Aug 2026 22:47:34 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/fMM6N28iCaLdXrusTQta3a-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&amp;#39;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A black Sony RX10 V bridge camera]]></media:description>                                                            <media:text><![CDATA[A black Sony RX10 V bridge camera]]></media:text>
                                <media:title type="plain"><![CDATA[A black Sony RX10 V bridge camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/fMM6N28iCaLdXrusTQta3a-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>The king of bridge cameras returns. Nearly a decade since the fourth-gen camera came out, Sony has introduced substantial improvements and upgrades to make the RX10 V a simply outstanding camera. With better ergonomics and controls, and bigger battery life, the RX10 V feels more like a mirrorless Alpha camera — and I’m not complaining.</p><p>Handling the RX10 V is a joy, and its long-reaching 24-600mm (full frame equivalent) lens lets you get up, close and personal with your subjects. Thanks to 30fps blackout-free drive and excellent autofocus, this is a solid camera for amateur wildlife and sports photographers. The 20.1MP images boast beautiful color reproduction, too.</p><p>Though not without its flaws, there aren’t many reasons to not buy this camera. To find out if this is the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best camera </a>for you, read my full Sony RX10 V review.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-specs"><span>Sony RX10 V review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.adorama.com/sony-dsc-rx10-v-digital-camera/p/isorx10m5" target="_blank" rel="nofollow">$2,298</a> / <a href="https://www.lcegroup.co.uk/New/Sony-RX10-V-Digital-Camera_234898.html" target="_blank" rel="nofollow">£2,199</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>20.1MP 1-inch Stacked Exmor RS CMOS</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>BIONZ XR</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Optical SteadyShot</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Fast Hybrid AF (phase-detection AF + contrast-detection AF)</p></td></tr><tr><td class="firstcol " ><p><strong>Focus modes</strong></p></td><td  ><p>Single Shot, Continuous AF, DMF, Manual focus</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>0.5-inch Quad-VGA OLED EVF, approx. 3.68 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3-inch TFT LCD touchscreen, approx. 1.6 million dots</p></td></tr><tr><td class="firstcol " ><p><strong>ISO range</strong></p></td><td  ><p>64-12,800</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>4K/60fps | 4K/120fps | FHD/60fps</p></td></tr><tr><td class="firstcol " ><p><strong>Ports</strong></p></td><td  ><p>USB-C, micro HDMI, USB Micro-B 2.0, 3.5mm mic in, 3.5mm headphones out, 1x SD/SDHC/SDXC UHS-I/II</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless/bluetooth</strong></p></td><td  ><p>Wi-Fi, Bluetooth</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>30fps (electronic) | 10fps (mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Max shutter speed</strong></p></td><td  ><p>30s to 1/16,000s (stills, electronic) | 30s to 1/2,000s (stills, mechanical)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>630 shots (LCD) | 570 shots (EVF)</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>5.37 x 3.72 x 5.96 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>2.7lbs (with battery and card)</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-sony-rx10-v-review-price-availability"><span>Sony RX10 V review: Price & availability</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D7njNQMomC9joTYj4eczBd" name="Sony_RX10_ 1.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/D7njNQMomC9joTYj4eczBd.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V has been nearly a decade in the making, and it succeeds the RX10 IV which first came out in 2017. The RX10 V is a bridge camera that literally bridges the gap between basic point-and-shoot compacts and full frame cameras, and it borrows features from Sony’s Alpha lineup (more on that shortly). It retails for <a href="https://www.adorama.com/sony-dsc-rx10-v-digital-camera/p/isorx10m5" target="_blank" rel="nofollow">$2,298 at Adorama U.S.</a> / <a href="https://www.lcegroup.co.uk/New/Sony-RX10-V-Digital-Camera_234898.html" target="_blank" rel="nofollow">£2,199 at London Camera Exchange U.K.</a></p><p>It’s pricier than the <a href="https://www.tomsguide.com/cameras-photography/nikon-coolpix-p1100-review">Nikon Coolpix P1100</a> ($1,146) and the Panasonic Lumix DMC-FZ2000/FZ2500 ($1,049) but it’s worth the extra spend if you want 30fps blackout-free burst shooting, fantastic autofocus, 4K/60fps video, and long battery life.</p><p>Spending nearly $3,000 on a camera may sound like a lot. But given that the camera features a built-in 24-600mm (full frame equivalent) lens, it negates the need to drop nearly a grand or more on a lens. This softens the blows a bit — especially because a telephoto lens on its own can set you back serious money. For context, a Sony 400-800mm lens costs over $3,000, while Canon’s RF1200mm lens costs well over $22,000.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-design"><span>Sony RX10 V review: Design</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D5jR7azS4uEABT7YnzZQ9d" name="Sony_RX10_ 3.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/D5jR7azS4uEABT7YnzZQ9d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Bridge cameras offer the convenience of point-and-shoot compacts and stuff it into DSLR-esque bodies. It’s no surprise, then, that the Sony RX10 V is slightly bulky, measuring 5.37 x 3.72 x 5.96 inches and weighing 2.7lbs (with battery and cards). It’s certainly bigger than my trusty mirrorless <a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-t50-review">Fujifilm X-T50</a> ($1,399) but even so, it’s more compact and lighter than the Nikon Coolpix P1100.</p><p>Unlike the Nikon Coolpix P1100, the RX10 V doesn’t feel as unwieldy to handle, thanks to its spacious protruding grip, which is deep and provides plenty of space for your fingers to wrap around it. Compared to the RX10 IV, the fifth-gen model offers better ergonomics, from its deeper grip to its more prominent command dials. The RX10 V feels like a modern Alpha camera, like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/i-spent-a-day-shooting-on-the-new-sony-a7-v-here-are-my-first-impressions-spoiler-alert-its-great">Sony A7 V</a> ($2,899). Build quality feels solid, too, and I’d expect nothing less from Sony. </p><h2 id="viewfinder">Viewfinder</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:879px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="Tk6ZE7ercUexT3wNHJ2X7d" name="Sony_RX10_ 7.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/v2/t:43,l:716,cw:879,ch:494,q:80/Tk6ZE7ercUexT3wNHJ2X7d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The RX10 V’s viewfinder has been upgraded. While its predecessor featured a 0.39-inch XGA OLED electronic viewfinder (EVF) with 0.7x magnification, the RX10 V packs a 0.5-inch Quad-VGA OLED EVF with 0.78x magnification — similar to the one on the flagship <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II </a>($6,499).</p><p>The resolution has received a bump, too, from 2.35 million dots on the RX10 IV to 3.68 million dots on the RX10 V. You now get a sharp, detailed, life-like representation of the scene you’re capturing. This helps you accurately frame and capture your subject. The RX10 V’s EVF resolution is similar to the Sony A7 V’s (3.69M dots), the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">Canon EOS R6 Mark III</a>’s (3.69M dots, $2,799) and the <a href="https://www.tomsguide.com/reviews/panasonic-lumix-g9-ii">Panasonic Lumix G9II</a>’s (3.68M dots, $1,899), though it’s slightly less than the Nikon Coolpix P1100’s (2.36M dots).</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:7365px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kJeKaFvJfxFCK8di2AFVra" name="Sony_RX10V" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/kJeKaFvJfxFCK8di2AFVra.jpg" mos="" align="middle" fullscreen="" width="7365" height="4143" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>I’ve found the RX10 V’s viewfinder extremely comfortable to look through, even through my prescription glasses. I also love that its refresh rate can be set to either 60Hz or 120Hz — the same as on the Canon EOS R6 Mark III and the much pricier <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r5-mark-ii-review">Canon EOS R5 Mark II</a> ($4,299). 120Hz refresh rate ensures that the subjects you’re tracking remain free of motion blur, and the RX10 V delivers smooth and realistic motion, making it great for fast-paced sports and wildlife photography.</p><p>The RX10 V borrows another feature from the Sony A7 V, and that’s 30fps blackout-free burst shooting. This means that there are no black screens between shots, enabling you to keep a constant view of your subject as you shoot. This is something we’ve also seen on other wildlife-oriented cameras, like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/om-system-om-1-mark-ii-review">OM System OM-1 Mark II</a> ($2,399), and the RX10 V wins brownie points over the Canon EOS R6 Mark III here which doesn’t feature blackout-free sequential shooting.</p><h2 id="monitor-2">Monitor</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="Tk6ZE7ercUexT3wNHJ2X7d" name="Sony_RX10_ 7.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/Tk6ZE7ercUexT3wNHJ2X7d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The RX10 V’s rear monitor has received a modest upgrade, too. While the screen size remains the same — 3-inch on both the RX10 IV and the RX10 V — the touchscreen now has a resolution of 1.6 million dots. The 3:2 aspect ratio also matches the camera’s sensor now, as opposed to the 4:3 aspect ratio on the RX10 V’s predecessor.</p><p>I’ve found the RX10 V’s monitor highly responsive in my testing, and I’ve had no trouble viewing it in different lighting conditions, including direct sunlight. It’s also much brighter than the Nikon Coolpix P1100’s 921K-dot monitor. The RX10 V’s screen can tilt up and down but it can’t hinge or tilt sideways, like the Nikon Coolpix P1100’s can, though, which limits the extent of shooting at awkward, low-to-the-ground or overhead angles.</p><h2 id="ports-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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="abHg4KmiWHT4D5i2uHMp6d" name="Sony_RX10_ 4.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/abHg4KmiWHT4D5i2uHMp6d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>All the ports are located on the RX10 V’s left edge and are protected by plastic flaps. You get a USB-C port for charging and data transfer; a legacy USB Micro-B port for accessories; a micro HDMI port; and dual 3.5mm ports for mic in and headphones out. The memory card slot is located next to the battery pack on the bottom plate.The RX10 V is compatible with a single SD/SDHC/SDXC UHS-I/II card.</p><h2 id="weather-sealing-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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="7ytCwpXxA3Gc7jmJpscjCd" name="Sony_RX10_ 5.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/7ytCwpXxA3Gc7jmJpscjCd.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Unlike the Nikon Coolpix P1100, the RX10 V is fully weather-sealed and protected against moisture and dust. This gives you more confidence when shooting outdoors and in changeable weather conditions.</p><p>The RX10 V’s weather-sealing comes at a cost, though — but not a huge one. There’s no built-in flash, like on previous iterations of the line, but a small pop-up flash is rarely powerful enough to light up distant subjects (perhaps macro subjects, yes), so it’s not something you’ll necessarily miss.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-controls"><span>Sony RX10 V review: Controls</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9RS5FmpcSe7aHkAiAmGy4d" name="Sony_RX10_ 11.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/9RS5FmpcSe7aHkAiAmGy4d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V is a delight to use, thanks to its well-placed and large buttons and dials. On the RX10 IV, an LCD screen took up a fair bit of space on the top plate, but this has been replaced by a prominent command dial that lets you swap between shooting modes as well as stills, video, and slow-and-quick (S&Q) video. You now get additional dials for shutter speed and ISO, too.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="wFs93caYnHmcFtxuY3Ud9d" name="Sony_RX10_ 13.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/wFs93caYnHmcFtxuY3Ud9d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>I like that the dual custom (C1 and C2) buttons have been moved up and are now located next to the shutter button. This makes accessing them much easier, and it makes shooting feel more intuitive. Sony has also added a physical focus joystick, similar to the one on its latest Alpha models, on the rear panel — something that was absent on the RX10 IV. The navigation wheel is bigger, too, and again, feels like it’s been borrowed from the A7 V or the A1 II.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="5C4qbvPkai7F8T7xRJzg6d" name="Sony_RX10_ 10.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/5C4qbvPkai7F8T7xRJzg6d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>These quality of life improvements result not only in better ergonomics, but also in a more well-rounded user experience. My only complaint is that the RX10 V’s three dials are only accessible by your right thumb, and I would have much preferred having a front dial for adjusting, say, shutter speed. Maybe sixth time’s the charm, eh, Sony?</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="35J5e8aib7AhbiUV38Yy6d" name="Sony_RX10_ 9.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/35J5e8aib7AhbiUV38Yy6d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>A decade is a long time, and it isn’t surprising that the RX10 IV’s menu system has been overhauled to deliver a more user-friendly experience on the RX 10 V. Instead of being text-heavy, the RX10 V’s menu system is color-coded, feels more logical and less cluttered, and it’s clearly been modeled after recent Alpha cameras.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-autofocus"><span>Sony RX10 V review: Autofocus</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="nkcaZDv8EKT9uRjAoukS8d" name="Sony_RX10_ 6.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/nkcaZDv8EKT9uRjAoukS8d.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V features an advanced hybrid autofocus system which uses a combination of phase- and contrast-detection AF, driven by the BIONZ XR processor. It inherits the Sony A7 V’s AI chip with real-time subject detection autofocus, and the Sony A1 II’s Auto Subject Detection which automatically swaps between human, bird/animal, insect, and vehicle detection modes.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/JFE9mn8hPpRNXpxfzF8wca.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/iRW75EF84nSRu3G2P2wW4d.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fVjajBpMmn5KoRLSdfW2mc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/8fmPW9UNj8EPUqrfaZYmBd.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/bFDXmBF9nrRLzKcQj3rTKa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4CpeZt9uE28bQJxuwVeoJb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Regardless of the subject I was photographing, the RX10 V didn’t let me down. As you can see in the gallery above, the camera was quick to find and lock on to birds, humans, and insects. When I was photographing birds and insects at a lake, I appreciated the camera’s Auto Subject Detection, and I found it very reliable. I was able to get the shots I needed as I didn’t have to waste time swapping between subjects manually.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/LXnxDGR9jCmMCNgcBzuaTc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/rr3xKP6dgojmVm3NVCwnaZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The photos above aren’t my finest work, but I wanted to use these two photos to demonstrate how well the RX10 V’s autofocus works. Both the squirrel and bird were obscured by leaves, and because of very bright sunlight, I couldn’t focus on them with my own eyes — so I pointed my camera right overhead and the RX10 V immediately locked on to them. <em>That’s </em>how smart the camera’s AF system is.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/4ptzqxEicPqDN7Fx5Tgvec.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/uAEw5jkaU6BDFKBxondJAd.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/euKhgZ5EbrzNd6JcUwPLcc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9bZFMauzQ4t7LDrmLo8LYc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I also appreciated the RX10 V locking on to and tracking the black-headed gull as I fired off a burst. The autofocus didn’t break at all, even as the gull landed on the water, surrounded by other birds. As you can see in the gallery above, it’s still in focus in the last two shots. </p><h2 class="article-body__section" id="section-sony-rx10-v-review-stabilization"><span>Sony RX10 V review: Stabilization</span></h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/iR74apVdMuhtB9pboSqCyX.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 1/3s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/MLd3MR7SrVTM6ktQgxZwUX.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 1/2s.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V utilizes Sony’s SteadyShot optical image stabilization which is also found in the <a href="https://www.tomsguide.com/cameras-photography/sony-zv-e10-ii-review">Sony ZV-E10 II</a> ($999) and the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-rx1r-iii-review">Sony RX1R III</a> ($5,099), and it incurs a crop. The lack of in-body image stabilization (IBIS) means you can’t shoot at super slow shutter speeds, but it should just about suffice when shooting handheld.</p><p>In my testing, I was able to shoot handheld at shutter speeds as slow as 1/3s (first image above) but any slower than that, like 1/2s (second image above), and the resulting image was blurry, impacted by camera shake. This is still good, though!</p><h2 class="article-body__section" id="section-sony-rx10-v-review-image-quality"><span>Sony RX10 V review: Image quality</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4867px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="9g4U23oDsUQ9m8i9Fn36ca" name="Sony_RX10V-2" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/9g4U23oDsUQ9m8i9Fn36ca.jpg" mos="" align="middle" fullscreen="" width="4867" height="2738" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>The Sony RX10 V packs a small 1-inch Stacked Exmor RS CMOS sensor with a 20.1MP resolution. Though small, its sensor is still bigger than the Nikon Coolpix P1100’s 1/2.3-inch 16MP one. You can’t make extreme crops as you can on, say, the 33MP full frame Sony A7 V, but given that you get a 24-600mm effective focal length, you can still capture detailed images of distant subjects.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/aKy3htXcWfzsPYo6ayoiic.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/NjLg2nEStFVDRLaPk4cVoc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 24mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Before we dive into image quality, let’s talk about the RX10 V’s reach. The RX10 V is fitted with a ZEISS Vario-Sonnar T 24-600mm lens, and this full frame equivalent focal length is achieved through its 25x optical zoom. In the gallery above, the first shot was taken at 600mm while the second at 24mm, and both images pack a lot of detail.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/koPJ2V2hcfjuC2mb4eMafa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken with 25x optical zoom + 4x digital zoom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/H2oqnmXrnH5vbjMMnuCJ7b.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QqfrYzXTy2HhQnT7dKXsVc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Taken at 24mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>In addition to the optical zoom, you also get a 4x digital zoom. Once the camera has reached 600mm, it applies a 4x digital zoom and starts cropping to achieve 1200mm. Naturally, this detrimentally impacts image quality as you're cropping into an already modest 20.1MP and losing resolution. The first image of the cormorants above was taken with the 4x digital zoom, while the second was taken with the 25x optical zoom, and the third is the fully zoomed out original. As you can see in the first slide, there is a drop in resolution and detail. I’d recommend sticking with the 25x optical zoom to get the best images.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/hVaTwpPRC58sNv4UdUt5YY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Extreme crop of second image.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QNfbtSaVopXsfE34GFmtHZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>Original image, taken at 600mm (full frame equivalent).<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Cropping after the fact in post-production yields similar results. The first shot in the gallery above is an extreme crop of the second photo, and once again, there's a drop in the quality. If you want a camera that allows for extreme cropping, you’ll have to splurge on the likes of the Sony A7 V or the Canon EOS R6 Mark III.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/9eVdVnGos5dnbD5nSWMSfZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mPgb9TWudejtMEjkwLVEZY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fTuAXvnhsCsAikTfzms9oa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/gFzAupsVop5uB2PeKAoQiZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/i94rhfK6zQpet95rQUSE3b.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YfTRcRwp2Wa5p4EugGf3iZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/wGVUM6KS5dse9vjBuUKjXZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/my5mooKexmgcqDcW2hq4dZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/uNfNRb8dsqJgef9dkX8LyY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pY2PMsAoGF7JgVGduFCpVZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6VuMVLqxwaMUrPntUoeMPb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/32Fyehx9h5z3vFTv6UKT8d.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Kacb443xQb6QGoHTxPHMvc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Fwgdx5JmnHRsmxQV5QEWc.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/vDh3WvT6h8qixbhFsLhQ6a.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Now that that’s out of the way, let’s finally talk about image quality — you can see sample shots in the gallery above. Color reproduction is stunning and accurate. The different shades and hues look beautiful, and I like that none of them look oversaturated or high on contrast. Even the neon-green algae looks vibrant but not overly bright.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/ufk6b4Eq5dSSbpfT35YNba.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zjgeDkaj2jD6ELkPwc8zra.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/EwTnnbN5dUausmF9cgmbpZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/DoAfnyVgzFH6dJzpanRJZa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V also packs 12 of Sony’s Creative Looks, which are in-camera filters or presets, similar to those you’ll find on Canon or Fujifilm cameras. These include but aren’t limited to Portrait, Film, Soft Highkey, and others. The images of the swans above were shot using the Black & White Creative Look, and I think they look gorgeous. I love how dramatic it makes the swans look, and the camera has also done a good job of capturing the fine details, such as the water droplets on the first swan’s head, and the tiny feathers on the last bird’s beak.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/jJnCanpf3nutuMBAq5yufa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/28itQ3AW9sY4UBSSMa2NQa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>For the above photos of the kingfisher, I opted for the Vivid Creative Look which boosts contrast, saturation and vibrancy. The resulting images appear punchy, fitting for such a colorful bird. You can also customize each Creative Look’s parameters, like the contrast, highlights, and shadows, giving you more flexibility.</p><h2 id="high-speed-drive">High-speed drive</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/M2vnTC2oXbcHPPubN2bLhb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/5VNJmWkpUvGhKtPZDcf3ib.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Lr6rWPSbxvBFAGm6HqoWZb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>30fps blackout-free sequential shooting via its electronic shutter is one of the RX10 V’s headline specs, and it’s seriously impressive. It can capture 546 JPEGs, 141 RAW and JPEGs, or 162 lossless RAW files when using the 10fps mechanical shutter. Having 30fps drive at your disposal means you can capture every single moment of action, making the RX10 V a solid choice for wildlife and sports. In comparison, the Nikon Coolpix P1100 has a measly 7fps drive.</p><p>The electronic shutter has a speed of 30s to 1/16,000s, and I bumped the speed up to 1/8,000s to capture the sequence of the coot flying towards the other. The mechanical shutter, on the other hand, tops out at 1/2,000s, so I’d recommend using the electronic shutter when you can to get blur-free images.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Qsd46T7eYFxnK2rYRGYKZZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eu6m2YBPRWp52KV34SUqZZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/YpQ2Mfqh9rbdpzBaTsKJhZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/nBTC6uqXS53oG7B4dyLUbZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2NcCD9RJeit4JjLJMr5oZZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ynqbZjmrxAWDdqb3guEQcZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/PsKoCGXt5o9CgddYoD3yfZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hxsdU6ZqVBAkowhXbo6qhZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/2Q3KCzpCH2f3kpjSr27MfZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jKtASKCJRdeKGdzrVszSgZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mt37knVkRTGxGrtjDt2vdZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Kd4Kbz8xTX6kqVViYuk2dZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Thanks to 30fps blackout-free burst and incredible autofocus that didn’t lose track of the tiny bird even once, I was able to capture the images (above) of the kingfisher taking off. The only downside to the RX10 V’s high-speed drive is that the camera doesn’t feature pre-burst capture, so it doesn’t start recording the frames when the shutter button is half-pressed. Again, you’ll need to get the Sony A7 V, the Canon EOS R6 Mark III or the OM System OM-1 Mark II if you want that.</p><h2 id="iso-performance">ISO performance</h2><p>The RX10 V doesn’t have as massive an ISO range as the one offered by the Sony A7 V — but it’s double the Nikon Coolpix P1100’s. The RX10 V has a base ISO of 64 and it tops out at 12,800. Given the camera’s small 1-inch sensor, high ISO performance isn’t anything to write home about.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/GmLNrK7Ta3aUWZGz8SYoLZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO8,000. JPEG export of ARW file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zErphYTPJERbUj8gu8UCWY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO8,000. SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The results were as expected at ISO6,400, so I bumped the ISO up to 8,000. The first image in the gallery above is a JPEG export of the ARW file, while the second is the straight-out-of-camera (SOOC) JPEG. While the in-camera noise reduction algorithms have done well to reduce the grain in the SOOC JPEG, the image has lost some sharpness around the <a href="https://www.tomsguide.com/audio/bluetooth-speakers/8bitdo-retro-cube-2-review">8BitDo Retro Cube 2</a>’s corners and edges, and the blue photobook looks too soft. </p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/6G2rgFCEavpmQUytTyEBWZ.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO12,800. JPEG export of ARW file.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ktPix7GYjNzQrTnQJVwKiY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>ISO12,800. SOOC JPEG.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>I then shot at ISO12,800. The first image (JPEG export of ARW file) has been ruined by noise, and the second shot (SOOC JPEG) isn’t that much better. You can see a lot of grain in the photobook as well as the speaker. The detail in the speaker’s grille has been lost, and the image appears flat. I wouldn’t recommend shooting at such high ISO unless and until it’s your last resort.</p><p>This is part and parcel of having a small sensor, but the RX10 V still performs better than the Nikon Coolpix P1100, whose 1/2.3-inch sensor produces extremely noisy images at just ISO6,400.</p><h2 id="dynamic-range">Dynamic range</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/QWHE5jxbL5cCqTTGCcJP9a.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO level 6, unedited.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/DqMaRZzFKdCuR9wyYyfwDa.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO level 6, shadows boosted and highlights reduced in Adobe Photoshop Lightroom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The RX10 V offers up to 9+ stops of dynamic range, and it features a D-Range Optimizer (DRO) mode which offers eight varying levels of balancing bright highlights and dark shadows.</p><p>The two images in the gallery above are the same shot, taken at DRO-6. The first image is the unedited RAW file. I boosted the shadows and reduced the highlights, which resulted in the second shot. Boosting the shadows has revealed a surprising amount of detail in the dark areas, such as the bushes in the distance. The shadows are fairly noise-free too, which is impressive.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/hxDyJjMNf3DUKCwNxSPvgb.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO disabled, unedited.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZsssQKvQzWhFs8ngoa5WyY.jpg" alt="A photo taken on a Sony RX10 V bridge camera" /><figcaption>DRO disabled, shadows boosted and highlights reduced in Adobe Photoshop Lightroom.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The backlit photo of the kingfisher above, on the other hand, was shot with DRO disabled. I edited the shot in Adobe Photoshop Lightroom and as you can see in the second slide, the camera has done a decent job of maintaining highlight detail while exposing for the bird’s comparatively dim unlit body. There’s some noticeable noise in the image, though, especially in the background (have a look at the bottom-left corner).</p><h2 class="article-body__section" id="section-sony-rx10-v-review-video"><span>Sony RX10 V review: Video</span></h2><div class="youtube-video" data-nosnippet ><div class="video-aspect-box"><iframe data-lazy-priority="high" data-lazy-src="https://www.youtube-nocookie.com/embed/FIVteoDdo2k" allowfullscreen></iframe></div></div><p>The Sony RX10 V is a competent hybrid camera, capable of recording 4K or FHD video at 60fps, and slow-mo 4K video at 120fps — the Nikon Coolpix P1100 can shoot 4K video at 30fps only. You can see a compilation of clips filmed at 4K/60fps above. I think the video looks great. Movement is fluid and smooth, while all the subjects remain in focus. The clips are fairly stable and not <em>too </em>shaky given that I was shooting handheld.</p><p>Similar to Sony’s vlogging cameras, you also get S-Log3 which captures a wide dynamic range, making it easier to color grade in post-production. If you want straight-out-of-camera slow-mo video, the camera’s slow-and-quick (S&Q) mode captures already slowed video (without any audio) which can be shared instantly without any need for editing in post.</p><h2 class="article-body__section" id="section-sony-rx10-v-review-battery-life"><span>Sony RX10 V review: Battery life</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5775px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="oUEaneDtJ5gCu34n37Vjga" name="Sony_RX10V-4" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/oUEaneDtJ5gCu34n37Vjga.jpg" mos="" align="middle" fullscreen="" width="5775" height="3248" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><p>Unlike the NP-FW50 battery pack used by the Sony RX10 IV, the Sony RX10 V utilizes the NP-FZ100 battery pack. It’s the same battery used by the likes of the Sony A1 II and the Sony A7 V. Thanks to the NP-FZ100 battery, the RX10 V now lasts longer than the RX10 IV. It’s CIPA-rated for 630 shots (versus 400) when using the monitor or 570 shots (versus 370) with the EVF.</p><p>CIPA testing is conducted under strict conditions and the ratings are usually conservative. More often than not, your camera will last you longer in the field. In my testing, I was able to get over 3,000 shots, which included a mix of burst sequences and RAW images, and I walked away with just under 50% battery. Take this with a pinch of salt, though, as your mileage will vary depending on your settings and shooting conditions.</p><p>The RX10 V lasts much longer than the Nikon Coolpix P1100 which is CIPA-rated for just 260 shots, and the Panasonic Lumix DMC-FZ2000/FZ2500 which yields 270 shots on a single charge. The RX10 V also upstages mirrorless beasts, like the Sony A1 II (520 shots) and the Canon EOS R6 Mark III (620 shots).</p><h2 class="article-body__section" id="section-sony-rx10-v-review-verdict"><span>Sony RX10 V review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="cJ9soApjYBhfmusZYHAHzc" name="Sony_RX10_.JPG" alt="A black Sony RX10 V bridge camera" src="https://cdn.mos.cms.futurecdn.net/cJ9soApjYBhfmusZYHAHzc.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want a powerful bridge camera that feels like a mirrorless camera, I can’t recommend the Sony RX10 V enough. It packs a long-reaching lens with a 25x optical zoom, letting you get close-up shots of far-away birds. The RX10 V feels sublime to use and handle, thanks to improved ergonomics and controls. It’s got long battery life to boot, too.</p><p>What makes the RX10 V worth the premium is its fantastic and accurate autofocus, as well as 30fps blackout-free high-speed drive. Combined, both of these features make the RX10 V a formidable camera for sports and wildlife photography — or videography, as the camera is capable of recording 4K/60fps video.</p><p>While the camera doesn’t feature the best high ISO performance, given its small 1-inch sensor that can’t take on full frame beasts, this small complaint is overshadowed by everything else on offer. The RX10 V blurs the line between bridge and Sony’s Alpha lineup to deliver one of the best cameras of 2026.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Gotta snap ’em all: Polaroid launches adorable new Pokémon collection ]]></title>
                                                                                                <dc:content><![CDATA[ <p>If you're looking for a highly portable instant camera, well, they don't get more portable than this... The Polaroid Go Generation 3 is the world's smallest analog instant camera. And if that wasn't cute enough, Polaroid has now teamed up with The Pokémon Company to offer this adorable collection of cameras and film based around some of its iconic characters.</p><p>There are two main Polaroid Go Generation 3 cameras to choose from here, each themed around Pikachu and Sylveon. There are also film packs and sets that include the camera, film, and a strap. If you're a fan then these are must-buys and they also make ideal gifts. Shop the collection below.</p>        <div class="featured_product_block featured_block_hero" data-id="25307326-a096-11f1-9f19-bb32a9690be6">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-pokemon-pikachu-edition" data-model-name="Go Generation 3 – Pokémon Pikachu Edition" 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/3BQX2LyXP3LqmAgN4saruY.jpg" alt="Polaroid Go Generation 3 – Pokémon Pikachu Edition"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Generation 3 – Pokémon Pikachu Edition</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="2530739e-a096-11f1-9a5a-8b3b214e532a">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-pokemon-sylveon-edition" data-model-name="Go Generation 3 – Pokémon Sylveon Edition" 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/MCcXnXKb8uw5hqF3GVUxSe.jpg" alt="Polaroid Go Generation 3 – Pokémon Pikachu Edition"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Generation 3 – Pokémon Sylveon Edition</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="25307402-a096-11f1-bce8-a9d6388863c9">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-color-film-double-pack-pokemon-pikachu-edition" data-model-name="Go Color Film – Pikachu Edition" 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/RMK2LVkF4TAs3HVEUfZesk.jpg" alt="Go Color Film – Pokémon Pikachu Edition – Double Pack"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Color Film – Pikachu Edition</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="25307470-a096-11f1-b1ff-5f4055ec4ddf">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-color-film-double-pack-pokemon-sylveon-edition" data-model-name="Go Color Film – Pokémon Sylveon Edition" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.93%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cq2kyP4BCG8iN9HUM54oJo.jpg" alt="Polaroid Go Color Film – Pokémon Sylveon Edition"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Color Film – Pokémon Sylveon Edition</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="253074d4-a096-11f1-8ab8-7778320e2ba1">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-gen-3-pikachu-edition-strap-set" data-model-name="Go Gen 3 Pikachu Edition Strap set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.93%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/q9haf3ReduS2uZem7hdX44.jpg" alt="Polaroid Go Gen 3 - Pikachu Edition Strap set"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Gen 3 Pikachu Edition Strap set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="25307538-a096-11f1-93b6-eb80a2828694">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-gen-3-sylveon-edition-strap-set" data-model-name="Go Gen 3 Sylveon Edition Strap Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.93%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/TpuPrF4FVTkomDLMDJVoo6.jpg" alt="Polaroid, Polaroid Go Gen 3 - Sylveon Edition Strap Set"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Gen 3 Sylveon Edition Strap Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/gotta-snap-em-all-polaroid-launches-adorable-new-pokemon-collection</link>
                                                                            <description>
                            <![CDATA[ The new collection of Polaroid Go Generation 3 cameras adds a super-cute Pokémon twist to the world's smallest instant analog camera. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">mDf66s5psyW5ZkRadSyvwi</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/sxSvt7aFXnUFjbHynvJPG8-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 25 Aug 2026 18:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras & 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/sxSvt7aFXnUFjbHynvJPG8-1280-80.jpg">
                                                            <media:credit><![CDATA[Polaroid, The Pokémon Company / Edited by Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A selection of Polaroid Go Generation 3 products.]]></media:description>                                                            <media:text><![CDATA[A selection of Polaroid Go Generation 3 products.]]></media:text>
                                <media:title type="plain"><![CDATA[A selection of Polaroid Go Generation 3 products.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/sxSvt7aFXnUFjbHynvJPG8-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>If you're looking for a highly portable instant camera, well, they don't get more portable than this... The Polaroid Go Generation 3 is the world's smallest analog instant camera. And if that wasn't cute enough, Polaroid has now teamed up with The Pokémon Company to offer this adorable collection of cameras and film based around some of its iconic characters.</p><p>There are two main Polaroid Go Generation 3 cameras to choose from here, each themed around Pikachu and Sylveon. There are also film packs and sets that include the camera, film, and a strap. If you're a fan then these are must-buys and they also make ideal gifts. Shop the collection below.</p>        <div class="featured_product_block featured_block_hero" data-id="25307326-a096-11f1-9f19-bb32a9690be6">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-pokemon-pikachu-edition" data-model-name="Go Generation 3 – Pokémon Pikachu Edition" 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/3BQX2LyXP3LqmAgN4saruY.jpg" alt="Polaroid Go Generation 3 – Pokémon Pikachu Edition"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Generation 3 – Pokémon Pikachu Edition</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="2530739e-a096-11f1-9a5a-8b3b214e532a">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-pokemon-sylveon-edition" data-model-name="Go Generation 3 – Pokémon Sylveon Edition" 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/MCcXnXKb8uw5hqF3GVUxSe.jpg" alt="Polaroid Go Generation 3 – Pokémon Pikachu Edition"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Generation 3 – Pokémon Sylveon Edition</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="25307402-a096-11f1-bce8-a9d6388863c9">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-color-film-double-pack-pokemon-pikachu-edition" data-model-name="Go Color Film – Pikachu Edition" 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/RMK2LVkF4TAs3HVEUfZesk.jpg" alt="Go Color Film – Pokémon Pikachu Edition – Double Pack"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Color Film – Pikachu Edition</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="25307470-a096-11f1-b1ff-5f4055ec4ddf">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-color-film-double-pack-pokemon-sylveon-edition" data-model-name="Go Color Film – Pokémon Sylveon Edition" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.93%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/cq2kyP4BCG8iN9HUM54oJo.jpg" alt="Polaroid Go Color Film – Pokémon Sylveon Edition"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Color Film – Pokémon Sylveon Edition</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="253074d4-a096-11f1-8ab8-7778320e2ba1">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-gen-3-pikachu-edition-strap-set" data-model-name="Go Gen 3 Pikachu Edition Strap set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.93%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/q9haf3ReduS2uZem7hdX44.jpg" alt="Polaroid Go Gen 3 - Pikachu Edition Strap set"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Gen 3 Pikachu Edition Strap set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="25307538-a096-11f1-93b6-eb80a2828694">            <a href="https://www.polaroid.com/en_us/products/polaroid-go-gen-3-sylveon-edition-strap-set" data-model-name="Go Gen 3 Sylveon Edition Strap Set" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:133.93%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/TpuPrF4FVTkomDLMDJVoo6.jpg" alt="Polaroid, Polaroid Go Gen 3 - Sylveon Edition Strap Set"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Gen 3 Sylveon Edition Strap Set</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ World Photography Day: 3 wildlife and landscape photographers explain why editing nature is a 'hazardous development' ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Photography means the world to me. I've loved it for as long as I can remember, and I love capturing the world around me. Every year, when World Photography Day comes around on 19 August, I can't help but think about the importance of visual storytelling. About the importance of documenting life and the world, and how this art form is a universal language.</p><p>But with developments in AI and the unprecedented rise of AI-generated images, it's becoming more and more difficult to distinguish between photorealistic images created by AI versus those created by humans. The issue goes even beyond being able to distinguish between the two, and as a wildlife photographer, it worries me that <a href="https://www.theguardian.com/environment/2026/jul/20/ai-slop-manipulated-fake-images-birds-citizen-science-aoe" target="_blank" rel="nofollow">AI-altered images of wildlife is are putting research and conservation at risk</a>.</p><p>Despite how I personally feel about the use of AI in photography, I asked myself, "How do professional photographers feel about it?" </p><p>So I reached out to three wildlife and landscape photographers and videographers — <a href="https://ettpust.com/about-1" target="_blank" rel="nofollow">Aleksander Nordahl</a>, <a href="https://sonvilla-graf.com/" target="_blank" rel="nofollow">Christine Sonvilla</a>, and <a href="https://sonvilla-graf.com/" target="_blank" rel="nofollow">Marc Graf</a> — who provided great insight into the use of AI in photography. Alongside that, they also shared what photography means to them, and advice they have for future generations.</p><h2 id="meet-the-photographers">Meet the photographers</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pPfMQKeJbEEzx6KAv734G9" name="WPD_interview_Aleksander" alt="A photo of photographer and freediver Aleksander Nordahl" src="https://cdn.mos.cms.futurecdn.net/pPfMQKeJbEEzx6KAv734G9.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Faine Loubser / Aleksander Nordahl)</span></figcaption></figure><p>Before we dive into the meat of this article, let's meet the three professional photographers I spoke to. Aleksander Nordahl is a visual journalist and the Managing Editor and Creative Director of <a href="https://sunstone.institute/" target="_blank" rel="nofollow">Sunstone Institute</a>, a global data science institute that conducts "data-driven studies on humanity's greatest threats and find compelling ways to tell the stories."</p><p>And that's exactly what Aleksander does, using underwater photography, videography and audio to show the impact of human activity on Norway's waters and coastline. His latest work, <a href="https://sunstone.institute/posts/a-journey-past-the-tipping-point" target="_blank" rel="nofollow">A Journey Past the Tipping Point</a>, explores the effects of nutrient pollution from fish farming, and I consider it to be essential watching for anyone — not just those who consider themselves nature lovers.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="C9ecZ58KhYTrhycXT2FLC9" name="WPD_interview_Christine_Marc" alt="Headshots of photographers Christine Sonvilla and Marc Graf" src="https://cdn.mos.cms.futurecdn.net/C9ecZ58KhYTrhycXT2FLC9.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Christine Sonvilla / Marc Graf / Bear Me In Mind Productions)</span></figcaption></figure><p>I also spoke with Christine Sonvilla and Marc Graf, filmmakers, photographers and conservationists, the geniuses behind <a href="https://sonvilla-graf.com/" target="_blank" rel="nofollow">Bear Me in Mind Productions</a> studio. Their studio aims to promote our relationship with nature and wildlife through TV documentaries, scientific shorts, and photographic assignments. They've documented <a href="https://sonvilla-graf.com/projects/croatian-vultures/" target="_blank" rel="nofollow">Eurasian griffon vulture,</a>, <a href="https://sonvilla-graf.com/projects/slovenia/" target="_blank" rel="nofollow">the Slovenian coast</a>, and <a href="https://sonvilla-graf.com/projects/bears/" target="_blank" rel="nofollow">brown bears in Central Europe</a>, to name a few projects.</p><p>Christine and Marc's work has earned them several accolades, including Wildlife Photographer of the Year. From human-wildlife interactions and specific ecological issues, to the return of large carnivores in Central Europe, their work has taken them all over the world.</p><p>As a wildlife photographer myself, Aleksander, Christine and Marc are great inspirations for me, as they tell stories that matter the most to the human race in an accessible and visually pleasing format.</p><h2 id="it-serves-as-a-personal-retreat-from-the-human-world">'It serves as a personal retreat from the human world'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4150px;"><p class="vanilla-image-block" style="padding-top:66.65%;"><img id="phE5JWGMQ8h7Sz6QxYprkS" name="Sony-A1-II-sample-27" alt="A photo of a seagull flying, taken on a Sony A1 II mirrorless camera and with a Sony FE 28-70mm F2 GM lens" src="https://cdn.mos.cms.futurecdn.net/phE5JWGMQ8h7Sz6QxYprkS.jpg" mos="" align="middle" fullscreen="" width="4150" height="2766" 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>Photography can mean a lot of different things to different people. For me, it's about capturing the wildlife and people around me, to keep as memories I can look back on when I'm older. It's also about highlighting specific birds that are often overlooked or hated by society, like pigeons — that's why my <a href="https://www.instagram.com/photos.bynikita" target="_blank" rel="nofollow">Pigeons of the South West series on Instagram</a> exists.</p><p>Visual storytelling can be a way to escape into another world, too, and Marc concurs. "It might sound egotistic, but what I appreciate most about photography is being exposed to wildlife, nature and the outdoors," he tells me. </p><p>"It serves as a personal retreat from the human world. Apart from this rather very personal meaning, I also consider a strong piece of photography the most powerful tool in communication since a photo allows to be consumed more than once and does not lose its power over time."</p><p>His partner, Christine, agrees. "There are two main reasons why I take photos: to communicate a message and to remember moments," she adds. "When viewing a photograph, I immediately dive back into the moment, not just visually but also with all other senses. Pictures are like a memory book to me. Initially, however, I started photography mainly to communicate messages I consider important. A powerful image sticks with people’s minds better than words alone."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4071px;"><p class="vanilla-image-block" style="padding-top:66.67%;"><img id="JRBdm9sveRvc59k48WyckN" name="Canon_EOS_R6_Mark_III-sample-36" alt="A photo taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/JRBdm9sveRvc59k48WyckN.jpg" mos="" align="middle" fullscreen="" width="4071" height="2714" 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 Christine and Marc mostly work with animals on land and birds in the sky, Aleksander dives deep underwater to capture a world not many of us have the pleasure of seeing — and he's been photographing for a long time.</p><p>"Photography is a really important part of my life, and it has been since I was 14 years old," he says. "It is my most important professional tool as a storyteller, journalist, artist, editor, investigator, and even freediver. But it also shapes how I see the world around me."</p><h2 id="to-capture-a-real-photo-today-is-to-show-the-world-like-it-really-is">'To capture a real photo today is to show the world like it really is'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6699px;"><p class="vanilla-image-block" style="padding-top:66.67%;"><img id="o5ihpTX4au5gnxiDY9AwPL" name="Canon_EOS_R6_Mark_III-sample-38" alt="A photo of a bird taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/o5ihpTX4au5gnxiDY9AwPL.jpg" mos="" align="middle" fullscreen="" width="6699" height="4466" 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 mentioned earlier, generative AI seems to have taken over most online spaces today — and it's having a detrimental impact on conservation. <a href="https://www.theguardian.com/environment/2026/jul/20/ai-slop-manipulated-fake-images-birds-citizen-science-aoe" target="_blank" rel="nofollow">According to The Guardian</a>, experts say that the increasing presence of these on birding platforms threaten credibility of tools used by scientists.</p><p>Since Christine, Marc and Aleksander's work focuses on conservation in one way or another, I had to ask them about what it means to capture a <em>real </em>photo in 2026. "I think the crucial point each photographer should question him- or herself is the following: Why am I photographing and what do I intend to achieve?" Christine muses. "In my case, it’s easy: If there is a gripping nature and wildlife topic, then I’d like to convey gripping imagery, which should be as authentic as possible.</p><p>"That means, I just do basic editing. The photo itself really happens out there. I don’t see a point in massively altering an image with the use of AI. I want to go out there and capture authentic moments. I personally consider it a hazardous development to overly alter or completely change nature and wildlife images with the use of AI. It distorts our understanding of nature, creates unattainable expectations and alienates us further and further from nature."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5602px;"><p class="vanilla-image-block" style="padding-top:66.67%;"><img id="LEDzqdedpS3C3xoBx2GciF" name="Canon EOS R5 Mark II - Sample 17" alt="A photo of a pigeon taken on a Canon EOS R5 Mark II camera." src="https://cdn.mos.cms.futurecdn.net/LEDzqdedpS3C3xoBx2GciF.jpg" mos="" align="middle" fullscreen="" width="5602" height="3735" 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>Marc agrees with that sentiment. "To me, it means the world! I consider photography the most individual, personal and intimate approach to a subject. And 'photography' to me means working with a camera in hand, with your brain developing ideas and finding ways to literally capture the right moment while at the same time your heart has to be convinced that this moment is the perfect one as well."</p><p>When I posed the same question to Aleksander, he shared a story. "The other day I saw a young photography influencer making an advertisement on social media showing how you could use his AI tool to make your concert photos even better. The example was removing some of the gear on stage, allowing the artist to stand out more. If you do that, can you still call it a real concert photo? Of course not."</p><p>Aleksander also said something that's going to stick with me for a long time: "To capture a real photo today is to show the world like it really is, in all its perfect and imperfect ways."</p><h2 id="what-i-see-and-document-is-what-you-get-nothing-more-nothing-less">'What I see and document is what you get: nothing more, nothing less'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4935px;"><p class="vanilla-image-block" style="padding-top:56.33%;"><img id="nx8rACcVvQWdYYtuBxZeG8" name="OM1-Mark-II-sample-22" alt="A photo taken on an OM System OM-1 Mark II Micro Four Thirds mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/nx8rACcVvQWdYYtuBxZeG8.jpg" mos="" align="middle" fullscreen="" width="4935" height="2780" 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>It's difficult to ignore the negative impact of generative AI and the <a href="https://www.tomsguide.com/ai/erin-brockovich-just-launched-an-ai-data-center-map-and-it-highlights-a-growing-problem-tech-giants-are-avoiding">increasing population of data centers</a> around the globe continues to receive backlash. All human needs and human activity, according to Aleksander, negatively impact the environment. "In Norway, where a vast amount of our electricity comes from hydropower, the demand for this and clean energy is increasing," he shares. "This will, of course, materialize in the increasing loss of nature. All my investigations into human activities in nature have been about the negative impacts of 'human needs.'"</p><p>But can AI actually be beneficial to workflows and post-production processes for photographers? "I work at an AI-native workplace," Aleksander answers when I pose the question to him. "Still, here we practice human input and human output. We let AI do the time-consuming, boring work, for instance, renaming and archiving visuals like photos and videos. But I never let AI make creative decisions on visuals for me. I will do that.</p><p>"We still need to make our own decisions. We must always control the parameters and set restrictions based on our human ethics and logic. Our visual storytelling is about real humans and the impact we have on real problems. We show them how it really is, and let our visual evidence speak for itself."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5184px;"><p class="vanilla-image-block" style="padding-top:56.33%;"><img id="qmZqwJ9Lis3Nzjbd2uP6s7" name="OM1-Mark-II-sample-9" alt="A photo taken on an OM System OM-1 Mark II Micro Four Thirds mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/qmZqwJ9Lis3Nzjbd2uP6s7.jpg" mos="" align="middle" fullscreen="" width="5184" height="2920" 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 Aleksander, his "line in the sand" is sticking to what he has always practiced since he was shooting on film. "What I see and document is what you get. Nothing more, nothing less. AI-generated/enhanced visuals have no place in my work as a documentarian. It can't. I am a witness. I am present when and where life unfolds. I record what is in front of me. That is what I show."</p><p>Christine and Marc have found some use for AI in post-production — but they never overuse it either. "I personally use very little AI in my photography editing workflow," Christine tells me. "One thing I find very helpful is AI-generated noise reduction. This works very well, however, each AI-generated step, like the noise-reduction, is very time-consuming, especially if these steps are being conducted for a multitude of images."</p><p>As someone who has tried using AI-powered noise reduction in Adobe Photoshop Lightroom, I can confirm that it takes a <em>very </em>long time — and for Christine, her prime credo is "spending the least amount of time necessary in front of the computer and working focused and diligently in the field, in order to reduce the necessity of more intensive editing."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2540px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Gyjer5UkV9bN6zDAxCcHDL" name="Canon_EOS_R6_Mark_III-sample-29" alt="A photo of a bird taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/v2/t:1390,l:0,cw:2540,ch:1429,q:80/Gyjer5UkV9bN6zDAxCcHDL.jpg" mos="" align="middle" fullscreen="" width="2540" height="3810" 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>Recently, the <a href="https://artificialintelligenceact.eu/article/50/" target="_blank" rel="nofollow">European Union has started enforcing transparency obligations</a> for tech companies, requiring them to clearly label any photos, videos or audio created using AI systems, designed to look authentic. I welcome this, and so does Marc, and he hopes that this extends to photography communities too. </p><p>"I don't use AI in post as I believe that any AI-enhancement should be labelled — the pure craft of photography lies in all our very own hands and not in the machines’ abilities to enhance or optimize our vision.</p><p>"And to be honest, I personally get bored very quickly by all those super perfect images out there — no matter if they are fully AI generated or over-optimized. Being imperfect is <em>the</em> most human thing, so why shouldn't we allow our images to mirror our imperfection?"</p><h2 id="don-t-just-aim-for-likes-and-attention-aim-for-meaning-respect-and-lasting-impact">'Don’t just aim for likes and attention; aim for meaning, respect and lasting impact'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:7728px;"><p class="vanilla-image-block" style="padding-top:56.21%;"><img id="SJjan8vDENtcJj4C2Hd4yY" name="Iceland-XT50-oped-19" alt="A photo of Iceland taken on a Fujifilm X-T50 mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/SJjan8vDENtcJj4C2Hd4yY.jpg" mos="" align="middle" fullscreen="" width="7728" height="4344" 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 an age of AI-saturated images, how do new photographers future-proof their work and their art? It's a question I keep asking myself, struggling to answer it, but thankfully, I had three pros share some great advice with me — and with you! Christine says one should ask themselves <em>why </em>they take photos. "Why do I take pictures, what’s my purpose and what audience do I want to reach?" she adds.</p><p>"For nature and wildlife photography, there should be a clear ethos, which is putting nature first. Massively altering images and conveying a distorted image of nature, which is either over-romanticized, over-glorified or falsified in terms of species integrity, is not doing nature a favor. Don’t just aim for likes and attention; aim for meaning, respect and lasting impact.</p><p>"AI can be helpful for one’s workflow and one certainly needs to acquaint oneself with it, but it must not be used to distort nature’s integrity. Less AI is more when it comes to nature and wildlife photography."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1544px;"><p class="vanilla-image-block" style="padding-top:66.65%;"><img id="UdLF3zPjpo4fZjpBkm3kVL" name="Sony-A1-II-sample-14" alt="A photo of a robin, taken on a Sony A1 II mirrorless camera and with a Sony FE 28-70mm F2 GM lens" src="https://cdn.mos.cms.futurecdn.net/UdLF3zPjpo4fZjpBkm3kVL.jpg" mos="" align="middle" fullscreen="" width="1544" height="1029" 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>Marc reflects on his 15-year-long career upon being asked this question, and as someone who has waited hours to get the perfect shot, I can't help but smile at his anecdote. "During my 15 plus years in the wildlife photo/film business, I have learned that most of the time and effort that drives my photography has to be invested in preparation, research, waiting and doing nothing but just crossing fingers that an image will be produced during a session.</p><p>"So, my advice is this: if you love what you are working on, carry a heavy backpack, and have invested in an expensive camera, then why not stay true to yourself and focus on capturing <em>the</em> moment. And if you do so, it's the most natural thing to be proud of your work. And it should be in all our photographers' interest to label our products as '100% human made.' A label that is still missing in the greater picture of woman/man-made photography but needed so much."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2140px;"><p class="vanilla-image-block" style="padding-top:66.68%;"><img id="m3k6CEYHNxn6Xd6cCF7a6N" name="Canon_EOS_R6_Mark_III-sample-14" alt="A photo taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/m3k6CEYHNxn6Xd6cCF7a6N.jpg" mos="" align="middle" fullscreen="" width="2140" height="1427" 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>Aleksander is doing all that he can to protect his work's authenticity too. "I use the new equipment and formats that safeguard the authenticity of my work, so you can backtrack what is done with my pictures," he says. "Maybe new photographers should do this too. Sell authenticity and don't rely on AI-driven shortcuts. Be the producer and documentarian of real life, in a market where you can make and get whatever you want. Maybe this is the future."</p><p>He also quotes his late colleague and war photographer Harald Henden: "The individual photographer's most important asset is credibility. When I put my name under a photograph, readers should be able to trust that the content is accurate. No further verification is needed."</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eMqM3e"></div>                            </div>                            <script src="https://kwizly.com/embed/eMqM3e.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/audio/we-are-rewind-pink-floyd-cassette-player-collab-ceo-interview">‘It’s more of a lifestyle, a fashionable device, not just tech’: We Are Rewind’s CEO on how the Pink Floyd collab is turning cassette players into ‘collector’s items’</a></li><li><a href="https://www.tomsguide.com/cameras-photography/a-love-letter-to-my-13-year-old-nikon-dslr">My 13-year-old Nikon DSLR has been battered, bruised, beaten and survived it all — it’s what made me fall in love with photography</a></li><li><a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">The best cameras in 2026</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/three-professional-photographers-on-ai-in-photography-interview-2026</link>
                                                                            <description>
                            <![CDATA[ I spoke to professional photographers Aleksander Nordahl, Christine Sonvilla and Marc Graf about AI in photography and their advice for future generations. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">ghgbKcDRmvB2CnetgRWFeE</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/m3k6CEYHNxn6Xd6cCF7a6N-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 19 Aug 2026 10:57:18 +0000</pubDate>                                                                                                                                <updated>Wed, 19 Aug 2026 15:36:22 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/m3k6CEYHNxn6Xd6cCF7a6N-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A photo taken on a Canon EOS R6 Mark III]]></media:description>                                                            <media:text><![CDATA[A photo taken on a Canon EOS R6 Mark III]]></media:text>
                                <media:title type="plain"><![CDATA[A photo taken on a Canon EOS R6 Mark III]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/m3k6CEYHNxn6Xd6cCF7a6N-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Photography means the world to me. I've loved it for as long as I can remember, and I love capturing the world around me. Every year, when World Photography Day comes around on 19 August, I can't help but think about the importance of visual storytelling. About the importance of documenting life and the world, and how this art form is a universal language.</p><p>But with developments in AI and the unprecedented rise of AI-generated images, it's becoming more and more difficult to distinguish between photorealistic images created by AI versus those created by humans. The issue goes even beyond being able to distinguish between the two, and as a wildlife photographer, it worries me that <a href="https://www.theguardian.com/environment/2026/jul/20/ai-slop-manipulated-fake-images-birds-citizen-science-aoe" target="_blank" rel="nofollow">AI-altered images of wildlife is are putting research and conservation at risk</a>.</p><p>Despite how I personally feel about the use of AI in photography, I asked myself, "How do professional photographers feel about it?" </p><p>So I reached out to three wildlife and landscape photographers and videographers — <a href="https://ettpust.com/about-1" target="_blank" rel="nofollow">Aleksander Nordahl</a>, <a href="https://sonvilla-graf.com/" target="_blank" rel="nofollow">Christine Sonvilla</a>, and <a href="https://sonvilla-graf.com/" target="_blank" rel="nofollow">Marc Graf</a> — who provided great insight into the use of AI in photography. Alongside that, they also shared what photography means to them, and advice they have for future generations.</p><h2 id="meet-the-photographers">Meet the photographers</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="pPfMQKeJbEEzx6KAv734G9" name="WPD_interview_Aleksander" alt="A photo of photographer and freediver Aleksander Nordahl" src="https://cdn.mos.cms.futurecdn.net/pPfMQKeJbEEzx6KAv734G9.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Faine Loubser / Aleksander Nordahl)</span></figcaption></figure><p>Before we dive into the meat of this article, let's meet the three professional photographers I spoke to. Aleksander Nordahl is a visual journalist and the Managing Editor and Creative Director of <a href="https://sunstone.institute/" target="_blank" rel="nofollow">Sunstone Institute</a>, a global data science institute that conducts "data-driven studies on humanity's greatest threats and find compelling ways to tell the stories."</p><p>And that's exactly what Aleksander does, using underwater photography, videography and audio to show the impact of human activity on Norway's waters and coastline. His latest work, <a href="https://sunstone.institute/posts/a-journey-past-the-tipping-point" target="_blank" rel="nofollow">A Journey Past the Tipping Point</a>, explores the effects of nutrient pollution from fish farming, and I consider it to be essential watching for anyone — not just those who consider themselves nature lovers.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="C9ecZ58KhYTrhycXT2FLC9" name="WPD_interview_Christine_Marc" alt="Headshots of photographers Christine Sonvilla and Marc Graf" src="https://cdn.mos.cms.futurecdn.net/C9ecZ58KhYTrhycXT2FLC9.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Christine Sonvilla / Marc Graf / Bear Me In Mind Productions)</span></figcaption></figure><p>I also spoke with Christine Sonvilla and Marc Graf, filmmakers, photographers and conservationists, the geniuses behind <a href="https://sonvilla-graf.com/" target="_blank" rel="nofollow">Bear Me in Mind Productions</a> studio. Their studio aims to promote our relationship with nature and wildlife through TV documentaries, scientific shorts, and photographic assignments. They've documented <a href="https://sonvilla-graf.com/projects/croatian-vultures/" target="_blank" rel="nofollow">Eurasian griffon vulture,</a>, <a href="https://sonvilla-graf.com/projects/slovenia/" target="_blank" rel="nofollow">the Slovenian coast</a>, and <a href="https://sonvilla-graf.com/projects/bears/" target="_blank" rel="nofollow">brown bears in Central Europe</a>, to name a few projects.</p><p>Christine and Marc's work has earned them several accolades, including Wildlife Photographer of the Year. From human-wildlife interactions and specific ecological issues, to the return of large carnivores in Central Europe, their work has taken them all over the world.</p><p>As a wildlife photographer myself, Aleksander, Christine and Marc are great inspirations for me, as they tell stories that matter the most to the human race in an accessible and visually pleasing format.</p><h2 id="it-serves-as-a-personal-retreat-from-the-human-world">'It serves as a personal retreat from the human world'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4150px;"><p class="vanilla-image-block" style="padding-top:66.65%;"><img id="phE5JWGMQ8h7Sz6QxYprkS" name="Sony-A1-II-sample-27" alt="A photo of a seagull flying, taken on a Sony A1 II mirrorless camera and with a Sony FE 28-70mm F2 GM lens" src="https://cdn.mos.cms.futurecdn.net/phE5JWGMQ8h7Sz6QxYprkS.jpg" mos="" align="middle" fullscreen="" width="4150" height="2766" 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>Photography can mean a lot of different things to different people. For me, it's about capturing the wildlife and people around me, to keep as memories I can look back on when I'm older. It's also about highlighting specific birds that are often overlooked or hated by society, like pigeons — that's why my <a href="https://www.instagram.com/photos.bynikita" target="_blank" rel="nofollow">Pigeons of the South West series on Instagram</a> exists.</p><p>Visual storytelling can be a way to escape into another world, too, and Marc concurs. "It might sound egotistic, but what I appreciate most about photography is being exposed to wildlife, nature and the outdoors," he tells me. </p><p>"It serves as a personal retreat from the human world. Apart from this rather very personal meaning, I also consider a strong piece of photography the most powerful tool in communication since a photo allows to be consumed more than once and does not lose its power over time."</p><p>His partner, Christine, agrees. "There are two main reasons why I take photos: to communicate a message and to remember moments," she adds. "When viewing a photograph, I immediately dive back into the moment, not just visually but also with all other senses. Pictures are like a memory book to me. Initially, however, I started photography mainly to communicate messages I consider important. A powerful image sticks with people’s minds better than words alone."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4071px;"><p class="vanilla-image-block" style="padding-top:66.67%;"><img id="JRBdm9sveRvc59k48WyckN" name="Canon_EOS_R6_Mark_III-sample-36" alt="A photo taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/JRBdm9sveRvc59k48WyckN.jpg" mos="" align="middle" fullscreen="" width="4071" height="2714" 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 Christine and Marc mostly work with animals on land and birds in the sky, Aleksander dives deep underwater to capture a world not many of us have the pleasure of seeing — and he's been photographing for a long time.</p><p>"Photography is a really important part of my life, and it has been since I was 14 years old," he says. "It is my most important professional tool as a storyteller, journalist, artist, editor, investigator, and even freediver. But it also shapes how I see the world around me."</p><h2 id="to-capture-a-real-photo-today-is-to-show-the-world-like-it-really-is">'To capture a real photo today is to show the world like it really is'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6699px;"><p class="vanilla-image-block" style="padding-top:66.67%;"><img id="o5ihpTX4au5gnxiDY9AwPL" name="Canon_EOS_R6_Mark_III-sample-38" alt="A photo of a bird taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/o5ihpTX4au5gnxiDY9AwPL.jpg" mos="" align="middle" fullscreen="" width="6699" height="4466" 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 mentioned earlier, generative AI seems to have taken over most online spaces today — and it's having a detrimental impact on conservation. <a href="https://www.theguardian.com/environment/2026/jul/20/ai-slop-manipulated-fake-images-birds-citizen-science-aoe" target="_blank" rel="nofollow">According to The Guardian</a>, experts say that the increasing presence of these on birding platforms threaten credibility of tools used by scientists.</p><p>Since Christine, Marc and Aleksander's work focuses on conservation in one way or another, I had to ask them about what it means to capture a <em>real </em>photo in 2026. "I think the crucial point each photographer should question him- or herself is the following: Why am I photographing and what do I intend to achieve?" Christine muses. "In my case, it’s easy: If there is a gripping nature and wildlife topic, then I’d like to convey gripping imagery, which should be as authentic as possible.</p><p>"That means, I just do basic editing. The photo itself really happens out there. I don’t see a point in massively altering an image with the use of AI. I want to go out there and capture authentic moments. I personally consider it a hazardous development to overly alter or completely change nature and wildlife images with the use of AI. It distorts our understanding of nature, creates unattainable expectations and alienates us further and further from nature."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5602px;"><p class="vanilla-image-block" style="padding-top:66.67%;"><img id="LEDzqdedpS3C3xoBx2GciF" name="Canon EOS R5 Mark II - Sample 17" alt="A photo of a pigeon taken on a Canon EOS R5 Mark II camera." src="https://cdn.mos.cms.futurecdn.net/LEDzqdedpS3C3xoBx2GciF.jpg" mos="" align="middle" fullscreen="" width="5602" height="3735" 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>Marc agrees with that sentiment. "To me, it means the world! I consider photography the most individual, personal and intimate approach to a subject. And 'photography' to me means working with a camera in hand, with your brain developing ideas and finding ways to literally capture the right moment while at the same time your heart has to be convinced that this moment is the perfect one as well."</p><p>When I posed the same question to Aleksander, he shared a story. "The other day I saw a young photography influencer making an advertisement on social media showing how you could use his AI tool to make your concert photos even better. The example was removing some of the gear on stage, allowing the artist to stand out more. If you do that, can you still call it a real concert photo? Of course not."</p><p>Aleksander also said something that's going to stick with me for a long time: "To capture a real photo today is to show the world like it really is, in all its perfect and imperfect ways."</p><h2 id="what-i-see-and-document-is-what-you-get-nothing-more-nothing-less">'What I see and document is what you get: nothing more, nothing less'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4935px;"><p class="vanilla-image-block" style="padding-top:56.33%;"><img id="nx8rACcVvQWdYYtuBxZeG8" name="OM1-Mark-II-sample-22" alt="A photo taken on an OM System OM-1 Mark II Micro Four Thirds mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/nx8rACcVvQWdYYtuBxZeG8.jpg" mos="" align="middle" fullscreen="" width="4935" height="2780" 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>It's difficult to ignore the negative impact of generative AI and the <a href="https://www.tomsguide.com/ai/erin-brockovich-just-launched-an-ai-data-center-map-and-it-highlights-a-growing-problem-tech-giants-are-avoiding">increasing population of data centers</a> around the globe continues to receive backlash. All human needs and human activity, according to Aleksander, negatively impact the environment. "In Norway, where a vast amount of our electricity comes from hydropower, the demand for this and clean energy is increasing," he shares. "This will, of course, materialize in the increasing loss of nature. All my investigations into human activities in nature have been about the negative impacts of 'human needs.'"</p><p>But can AI actually be beneficial to workflows and post-production processes for photographers? "I work at an AI-native workplace," Aleksander answers when I pose the question to him. "Still, here we practice human input and human output. We let AI do the time-consuming, boring work, for instance, renaming and archiving visuals like photos and videos. But I never let AI make creative decisions on visuals for me. I will do that.</p><p>"We still need to make our own decisions. We must always control the parameters and set restrictions based on our human ethics and logic. Our visual storytelling is about real humans and the impact we have on real problems. We show them how it really is, and let our visual evidence speak for itself."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5184px;"><p class="vanilla-image-block" style="padding-top:56.33%;"><img id="qmZqwJ9Lis3Nzjbd2uP6s7" name="OM1-Mark-II-sample-9" alt="A photo taken on an OM System OM-1 Mark II Micro Four Thirds mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/qmZqwJ9Lis3Nzjbd2uP6s7.jpg" mos="" align="middle" fullscreen="" width="5184" height="2920" 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 Aleksander, his "line in the sand" is sticking to what he has always practiced since he was shooting on film. "What I see and document is what you get. Nothing more, nothing less. AI-generated/enhanced visuals have no place in my work as a documentarian. It can't. I am a witness. I am present when and where life unfolds. I record what is in front of me. That is what I show."</p><p>Christine and Marc have found some use for AI in post-production — but they never overuse it either. "I personally use very little AI in my photography editing workflow," Christine tells me. "One thing I find very helpful is AI-generated noise reduction. This works very well, however, each AI-generated step, like the noise-reduction, is very time-consuming, especially if these steps are being conducted for a multitude of images."</p><p>As someone who has tried using AI-powered noise reduction in Adobe Photoshop Lightroom, I can confirm that it takes a <em>very </em>long time — and for Christine, her prime credo is "spending the least amount of time necessary in front of the computer and working focused and diligently in the field, in order to reduce the necessity of more intensive editing."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2540px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Gyjer5UkV9bN6zDAxCcHDL" name="Canon_EOS_R6_Mark_III-sample-29" alt="A photo of a bird taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/v2/t:1390,l:0,cw:2540,ch:1429,q:80/Gyjer5UkV9bN6zDAxCcHDL.jpg" mos="" align="middle" fullscreen="" width="2540" height="3810" 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>Recently, the <a href="https://artificialintelligenceact.eu/article/50/" target="_blank" rel="nofollow">European Union has started enforcing transparency obligations</a> for tech companies, requiring them to clearly label any photos, videos or audio created using AI systems, designed to look authentic. I welcome this, and so does Marc, and he hopes that this extends to photography communities too. </p><p>"I don't use AI in post as I believe that any AI-enhancement should be labelled — the pure craft of photography lies in all our very own hands and not in the machines’ abilities to enhance or optimize our vision.</p><p>"And to be honest, I personally get bored very quickly by all those super perfect images out there — no matter if they are fully AI generated or over-optimized. Being imperfect is <em>the</em> most human thing, so why shouldn't we allow our images to mirror our imperfection?"</p><h2 id="don-t-just-aim-for-likes-and-attention-aim-for-meaning-respect-and-lasting-impact">'Don’t just aim for likes and attention; aim for meaning, respect and lasting impact'</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:7728px;"><p class="vanilla-image-block" style="padding-top:56.21%;"><img id="SJjan8vDENtcJj4C2Hd4yY" name="Iceland-XT50-oped-19" alt="A photo of Iceland taken on a Fujifilm X-T50 mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/SJjan8vDENtcJj4C2Hd4yY.jpg" mos="" align="middle" fullscreen="" width="7728" height="4344" 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 an age of AI-saturated images, how do new photographers future-proof their work and their art? It's a question I keep asking myself, struggling to answer it, but thankfully, I had three pros share some great advice with me — and with you! Christine says one should ask themselves <em>why </em>they take photos. "Why do I take pictures, what’s my purpose and what audience do I want to reach?" she adds.</p><p>"For nature and wildlife photography, there should be a clear ethos, which is putting nature first. Massively altering images and conveying a distorted image of nature, which is either over-romanticized, over-glorified or falsified in terms of species integrity, is not doing nature a favor. Don’t just aim for likes and attention; aim for meaning, respect and lasting impact.</p><p>"AI can be helpful for one’s workflow and one certainly needs to acquaint oneself with it, but it must not be used to distort nature’s integrity. Less AI is more when it comes to nature and wildlife photography."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1544px;"><p class="vanilla-image-block" style="padding-top:66.65%;"><img id="UdLF3zPjpo4fZjpBkm3kVL" name="Sony-A1-II-sample-14" alt="A photo of a robin, taken on a Sony A1 II mirrorless camera and with a Sony FE 28-70mm F2 GM lens" src="https://cdn.mos.cms.futurecdn.net/UdLF3zPjpo4fZjpBkm3kVL.jpg" mos="" align="middle" fullscreen="" width="1544" height="1029" 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>Marc reflects on his 15-year-long career upon being asked this question, and as someone who has waited hours to get the perfect shot, I can't help but smile at his anecdote. "During my 15 plus years in the wildlife photo/film business, I have learned that most of the time and effort that drives my photography has to be invested in preparation, research, waiting and doing nothing but just crossing fingers that an image will be produced during a session.</p><p>"So, my advice is this: if you love what you are working on, carry a heavy backpack, and have invested in an expensive camera, then why not stay true to yourself and focus on capturing <em>the</em> moment. And if you do so, it's the most natural thing to be proud of your work. And it should be in all our photographers' interest to label our products as '100% human made.' A label that is still missing in the greater picture of woman/man-made photography but needed so much."</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2140px;"><p class="vanilla-image-block" style="padding-top:66.68%;"><img id="m3k6CEYHNxn6Xd6cCF7a6N" name="Canon_EOS_R6_Mark_III-sample-14" alt="A photo taken on a Canon EOS R6 Mark III" src="https://cdn.mos.cms.futurecdn.net/m3k6CEYHNxn6Xd6cCF7a6N.jpg" mos="" align="middle" fullscreen="" width="2140" height="1427" 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>Aleksander is doing all that he can to protect his work's authenticity too. "I use the new equipment and formats that safeguard the authenticity of my work, so you can backtrack what is done with my pictures," he says. "Maybe new photographers should do this too. Sell authenticity and don't rely on AI-driven shortcuts. Be the producer and documentarian of real life, in a market where you can make and get whatever you want. Maybe this is the future."</p><p>He also quotes his late colleague and war photographer Harald Henden: "The individual photographer's most important asset is credibility. When I put my name under a photograph, readers should be able to trust that the content is accurate. No further verification is needed."</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-eMqM3e"></div>                            </div>                            <script src="https://kwizly.com/embed/eMqM3e.js" async></script><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/audio/we-are-rewind-pink-floyd-cassette-player-collab-ceo-interview">‘It’s more of a lifestyle, a fashionable device, not just tech’: We Are Rewind’s CEO on how the Pink Floyd collab is turning cassette players into ‘collector’s items’</a></li><li><a href="https://www.tomsguide.com/cameras-photography/a-love-letter-to-my-13-year-old-nikon-dslr">My 13-year-old Nikon DSLR has been battered, bruised, beaten and survived it all — it’s what made me fall in love with photography</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[ To celebrate World Photography Day, I turned my favorite nature photo into a virtual jigsaw puzzle you can solve now ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As a former editor of Canon's professional photography website, World Photography Day has been a highlight of my summer for (checks calendar) almost 10 years. Of course, celebrating the craft behind capturing a photo that speaks to people is something we can do every day. But there's something special about having a whole global day dedicated to photography that delights me.</p><p>This year, rather than interviewing professional photographers creating across the world, I looked to my friend and colleague <a href="https://www.tomsguide.com/author/nikita-achanta">Nikita Achanta</a>, whose photos never fail to delight me. Here I've selected my favorite shot of hers, an ethereal-looking sea bird soaring above golden-tinted waters, and have turned it into a virtual jigsaw puzzle that you can play for free, below.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-e47ZbX"></div>                            </div>                            <script src="https://kwizly.com/embed/e47ZbX.js" async></script><p>I'm a sucker for nature photography, and particularly love this photo because of the way the light transforms the subject. The sunlight gently kissing the gull's head and wing tip show the softness of its feathers and the graceful arc of its wings. That same sunlight turns the ripples of the river to liquid gold.</p><p>Nikita's composition makes the most of this magical light. The positioning of the gull over the point where the river changes from blue to gold almost makes us imagine that the gull is painting the water with its feathers.</p><p>The light and composition –– which Nikita captured on the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II</a>, one of the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras</a> –– create an image with a magical, ethereal quality. The humble black-headed gull is shown as an elegant creature, not the pest that they're sometimes regarded as.</p><p>Nikita says that's one of the things she loves about her photo, too. "Seagulls, black-headed gulls, and essentially all gulls, get a bad reputation, especially here in the U.K. where I'm based. While I've had my fair share of food stolen by city gulls, I also think they're very pretty and are often overlooked in society (kind of like pigeons)," she says. "This photo of the black-headed gull is one of my favorite shots I've taken, as it shows off the different patterns and shades in the feathers, and draws attention to the bright orange beak."</p><p>In addition to being a beautiful photo, the expanse of water surrounding the gull makes this a fantastic jigsaw puzzle image. When I tested it out, even the Easy level gave me pause for thought, as I lost myself in the textural details of the water when trying to match up the edges.</p><h2 class="article-body__section" id="section-how-to-play-our-online-jigsaw-puzzles"><span>How to play our online jigsaw puzzles</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:609px;"><p class="vanilla-image-block" style="padding-top:73.89%;"><img id="MoyiaTqURqE8dCkUcMccc" name="Screenshot 2026-03-25 at 13.03.04" alt="A screenshot showing the playing experience of a Tom's Guide jigsaw" src="https://cdn.mos.cms.futurecdn.net/MoyiaTqURqE8dCkUcMccc.png" mos="" align="middle" fullscreen="" width="609" height="450" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Help buttons can assist you  </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>As you'll see above, before you hit 'Play', you can choose between Easy (12 pieces), Medium (20 pieces) and Hard (35 pieces) difficulty levels.</p><p>You can also make use of the helpful buttons along the bottom to help you complete the puzzle. ‘Edge Pieces Only’ clears the clutter so you can get those corners and the outer frame perfected first, which is always my preferred system for completing jigsaw puzzles in real life, too. </p><p>‘Reveal Puzzle Image’ gives you a sheer version of the final image that you can lay the pieces directly onto. ‘Place Next Piece’ does exactly that, acting as a friend or puzzle partner. </p><p>Whether you use those help buttons or not, you’ll see when you’ve placed a piece in the correct position as the number at the top will increase, taking you closer to full marks and a place on the leaderboard. </p><p>We have plenty of other virtual jigsaws available and more to come, so if you enjoy puzzles then do bookmark the <a href="https://www.tomsguide.com/play/type/Aeyae2">Jigsaws section</a> of our <a href="https://www.tomsguide.com/play">Puzzles & Quizzes</a> page for more as they arrive.</p><p>How did you do? Let us know what you think in the comments below –– including any other jigsaw puzzles you'd love to see.</p><p>Don't forget to register on our quizzes, games and polls platform, Kwizly, to save your score, reveal hints, and secure your spot on the competitive Tom's Guide leaderboard, and see the results of all our <a href="https://www.tomsguide.com/tag/polls">polls</a> in real-time.</p><p>And if you love jigsaw puzzles, you may be interested in splashing out on some analog jigsaw puzzles to play at home. </p><h3 class="article-body__section" id="section-shop-jigsaw-puzzles"><span>Shop jigsaw puzzles</span></h3>        <div class="featured_product_block featured_block_hero" data-id="44db755e-9ae7-11f1-a657-77b03cb8de4f">            <a href="https://www.amazon.com/1000-Piece-Puzzle-Adults-Paper-Cutting/dp/B0FZB4RJCM/" data-model-name="1000 Piece 3D Puzzle Cave Forest Jigsaw Puzzle" 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/fymvNyNGv52p4vuUwe9EG3.jpg" alt="1000 Piece Puzzle for Adults - 3d Cave Forest Jigsaw Puzzles Paper-Cutting Art - Brain Teaser Adult Puzzles -Impossible Hard Difficult Challenging Puzzle- Christmas Birthday Gifts for Women/men"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">1000 Piece 3D Puzzle Cave Forest Jigsaw Puzzle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="44db75c2-9ae7-11f1-8c36-6f1fa66c5ea7">            <a href="https://www.amazon.com/PICKFORU-Impossible-Difficult-Challenging-Colorful/dp/B0D13Z9HGV/" data-model-name="1000 Piece Mosaic Tree of Life Jigsaw Puzzle" 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/S6mF7JtfLTqbpwifWfYHJ3.jpg" alt="Pickforu Stained Glass Puzzle Tree of Life Puzzles for Adults 1000 Pieces, Impossible Hard Difficult Challenging Puzzles for Adults, Colorful Mosaic Tree of Life Jigsaw Puzzle 1000 Pieces"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">1000 Piece Mosaic Tree of Life Jigsaw Puzzle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="44db7626-9ae7-11f1-a879-17f92a2c9501">            <a href="https://www.amazon.com/National-Landscape-Including-Yellowstone-Yosemite/dp/B0BFDDYZCW/" data-model-name="1000 Piece US National Parks Jigsaw Puzzle" 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/QWYxsDQubKcpSC832hznE3.jpg" alt="National Parks Puzzle for Adults 1000 Pieces, Travel Poster Landscape Puzzle Including Zion Yellowstone Yosemite, Nature Jigsaw Puzzles Scenery Mountain Scene"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">1000 Piece US National Parks Jigsaw Puzzle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="44db777a-9ae7-11f1-9ae3-9542e21f4918">            <a href="https://www.amazon.com/Rubber-Puzzle-Mat-Jigsaw-Pieces/dp/B0FSG189LJ/" data-model-name="Portable Roll-Up Jigsaw Puzzle Mat with 4 Sorting Trays" 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/q4BVeiKUxtsCrjHy8z5aJ3.jpg" alt="Rubber Puzzle Mat- Jigsaw Puzzle Mat Roll Up for 1000 and 1500 Pieces - Portable Mat With 4 Sorting Trays - Non-Slip Rubber Bottom Storage and Keeper - 46 X 26 Inches"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Portable Roll-Up Jigsaw Puzzle Mat with 4 Sorting Trays</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/entertainment/movies/classic-movie-lovers-take-our-quiz-to-see-how-much-you-really-know-from-indiana-jones-to-star-wars">Classic movie lovers, take our quiz to see how much you really know — from ‘Indiana Jones’ to ‘Star Wars’</a></li><li><a href="https://www.tomsguide.com/gaming/toms-guide-jigsaw-puzzles-hardest-difficulty-has-me-stumped">Can you beat us at our own game? Tom’s Guide jigsaw puzzles are a great way to kill time between Wordles</a></li><li><a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">The best cameras in 2026</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/to-celebrate-world-photography-day-i-turned-my-favorite-nature-photo-into-a-virtual-jigsaw-puzzle-you-can-solve-now</link>
                                                                            <description>
                            <![CDATA[ This ethereal sea bird is my favorite shot by my photographer colleague, so I made it into an online jigsaw –– if you can solve the 'Hard' level, you're a puzzle master ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">HyQNDdxDCWqDoxvv53QRGE</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/RE3E8JeL8XmPVjkwLhFgya-1280-80.png" type="image/png" length="0"></enclosure>
                                                                        <pubDate>Wed, 19 Aug 2026 09:02:00 +0000</pubDate>                                                                                                                                <updated>Thu, 20 Aug 2026 10:41:16 +0000</updated>
                                                                                                                                            <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ ella.taylor@futurenet.com (Ella Taylor) ]]></author>                    <dc:creator><![CDATA[ Ella Taylor ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/W2nC7HFA6avQHdFNFbFPNa.jpg ]]></dc:source>
                                                                <dc:description><![CDATA[ &lt;p&gt;Ella is Tom’s Guide’s Engagement Editor, on a mission to get to know all about our readers, to help you with your burning questions, and to create an exciting mix of content that entices you to keep coming back to the site – whether you’re a regular reader or a &lt;a href=&quot;https://www.tomsguide.com/membership&quot;&gt;member&lt;/a&gt;. &lt;/p&gt;&lt;p&gt;She has more than 15 years of editorial experience, graduating from Kingston University with a First Class Honours degree in Journalism in 2008. She has written for and edited magazines and websites, including fellow Future titles &lt;em&gt;TechRadar.com, CreativeBloq.com, Digital Camera magazine, NPhoto, BikeRadar.com, Mollie Makes&lt;/em&gt; and more. &lt;/p&gt;&lt;p&gt;Most recently, she was Managing Editor of &lt;em&gt;womanandhome.com&lt;/em&gt;, where she co-launched its &lt;a href=&quot;https://www.womanandhome.com/tag/quiz/&quot; target=&quot;_blank&quot;&gt;Quiz of the Day&lt;/a&gt; and led the charge for articles that actively asked readers for their views. Before that, she was Editor of professional photography website &lt;em&gt;canon-europe.com/pro&lt;/em&gt;, having worked her way up from Production Editor. As such, she&#039;s a stickler for fact-checking, has too many opinions about grammar and is easily excited by a beautiful photo.&lt;/p&gt;&lt;p&gt;A mum of two young children, she keeps fit by typically carrying at least one of them on her shoulders around the hilly streets of Bristol. When not chasing her sons around the play parks and soft plays of South West England and worshipping her three rescue cats, she&#039;s likely to be in the garden, steadily turning it from a neglected concrete jungle to a usable green space. She’s also finally embarking on a kitchen extension in May, so 2026 looks set to be a year of big home improvements and lots of builders’ tea.&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/RE3E8JeL8XmPVjkwLhFgya-1280-80.png">
                                                            <media:credit><![CDATA[Nikita Achanta/Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A photo of a graceful seabird sweeping over water, with a &#039;Tom&#039;s Guide Games&#039; logo overlaid]]></media:description>                                                            <media:text><![CDATA[A photo of a graceful seabird sweeping over water, with a &#039;Tom&#039;s Guide Games&#039; logo overlaid]]></media:text>
                                <media:title type="plain"><![CDATA[A photo of a graceful seabird sweeping over water, with a &#039;Tom&#039;s Guide Games&#039; logo overlaid]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/RE3E8JeL8XmPVjkwLhFgya-1280-80.png" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As a former editor of Canon's professional photography website, World Photography Day has been a highlight of my summer for (checks calendar) almost 10 years. Of course, celebrating the craft behind capturing a photo that speaks to people is something we can do every day. But there's something special about having a whole global day dedicated to photography that delights me.</p><p>This year, rather than interviewing professional photographers creating across the world, I looked to my friend and colleague <a href="https://www.tomsguide.com/author/nikita-achanta">Nikita Achanta</a>, whose photos never fail to delight me. Here I've selected my favorite shot of hers, an ethereal-looking sea bird soaring above golden-tinted waters, and have turned it into a virtual jigsaw puzzle that you can play for free, below.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-e47ZbX"></div>                            </div>                            <script src="https://kwizly.com/embed/e47ZbX.js" async></script><p>I'm a sucker for nature photography, and particularly love this photo because of the way the light transforms the subject. The sunlight gently kissing the gull's head and wing tip show the softness of its feathers and the graceful arc of its wings. That same sunlight turns the ripples of the river to liquid gold.</p><p>Nikita's composition makes the most of this magical light. The positioning of the gull over the point where the river changes from blue to gold almost makes us imagine that the gull is painting the water with its feathers.</p><p>The light and composition –– which Nikita captured on the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">Sony A1 II</a>, one of the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras</a> –– create an image with a magical, ethereal quality. The humble black-headed gull is shown as an elegant creature, not the pest that they're sometimes regarded as.</p><p>Nikita says that's one of the things she loves about her photo, too. "Seagulls, black-headed gulls, and essentially all gulls, get a bad reputation, especially here in the U.K. where I'm based. While I've had my fair share of food stolen by city gulls, I also think they're very pretty and are often overlooked in society (kind of like pigeons)," she says. "This photo of the black-headed gull is one of my favorite shots I've taken, as it shows off the different patterns and shades in the feathers, and draws attention to the bright orange beak."</p><p>In addition to being a beautiful photo, the expanse of water surrounding the gull makes this a fantastic jigsaw puzzle image. When I tested it out, even the Easy level gave me pause for thought, as I lost myself in the textural details of the water when trying to match up the edges.</p><h2 class="article-body__section" id="section-how-to-play-our-online-jigsaw-puzzles"><span>How to play our online jigsaw puzzles</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:609px;"><p class="vanilla-image-block" style="padding-top:73.89%;"><img id="MoyiaTqURqE8dCkUcMccc" name="Screenshot 2026-03-25 at 13.03.04" alt="A screenshot showing the playing experience of a Tom's Guide jigsaw" src="https://cdn.mos.cms.futurecdn.net/MoyiaTqURqE8dCkUcMccc.png" mos="" align="middle" fullscreen="" width="609" height="450" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">Help buttons can assist you  </span><span class="credit" itemprop="copyrightHolder">(Image credit: Future)</span></figcaption></figure><p>As you'll see above, before you hit 'Play', you can choose between Easy (12 pieces), Medium (20 pieces) and Hard (35 pieces) difficulty levels.</p><p>You can also make use of the helpful buttons along the bottom to help you complete the puzzle. ‘Edge Pieces Only’ clears the clutter so you can get those corners and the outer frame perfected first, which is always my preferred system for completing jigsaw puzzles in real life, too. </p><p>‘Reveal Puzzle Image’ gives you a sheer version of the final image that you can lay the pieces directly onto. ‘Place Next Piece’ does exactly that, acting as a friend or puzzle partner. </p><p>Whether you use those help buttons or not, you’ll see when you’ve placed a piece in the correct position as the number at the top will increase, taking you closer to full marks and a place on the leaderboard. </p><p>We have plenty of other virtual jigsaws available and more to come, so if you enjoy puzzles then do bookmark the <a href="https://www.tomsguide.com/play/type/Aeyae2">Jigsaws section</a> of our <a href="https://www.tomsguide.com/play">Puzzles & Quizzes</a> page for more as they arrive.</p><p>How did you do? Let us know what you think in the comments below –– including any other jigsaw puzzles you'd love to see.</p><p>Don't forget to register on our quizzes, games and polls platform, Kwizly, to save your score, reveal hints, and secure your spot on the competitive Tom's Guide leaderboard, and see the results of all our <a href="https://www.tomsguide.com/tag/polls">polls</a> in real-time.</p><p>And if you love jigsaw puzzles, you may be interested in splashing out on some analog jigsaw puzzles to play at home. </p><h3 class="article-body__section" id="section-shop-jigsaw-puzzles"><span>Shop jigsaw puzzles</span></h3>        <div class="featured_product_block featured_block_hero" data-id="44db755e-9ae7-11f1-a657-77b03cb8de4f">            <a href="https://www.amazon.com/1000-Piece-Puzzle-Adults-Paper-Cutting/dp/B0FZB4RJCM/" data-model-name="1000 Piece 3D Puzzle Cave Forest Jigsaw Puzzle" 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/fymvNyNGv52p4vuUwe9EG3.jpg" alt="1000 Piece Puzzle for Adults - 3d Cave Forest Jigsaw Puzzles Paper-Cutting Art - Brain Teaser Adult Puzzles -Impossible Hard Difficult Challenging Puzzle- Christmas Birthday Gifts for Women/men"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">1000 Piece 3D Puzzle Cave Forest Jigsaw Puzzle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="44db75c2-9ae7-11f1-8c36-6f1fa66c5ea7">            <a href="https://www.amazon.com/PICKFORU-Impossible-Difficult-Challenging-Colorful/dp/B0D13Z9HGV/" data-model-name="1000 Piece Mosaic Tree of Life Jigsaw Puzzle" 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/S6mF7JtfLTqbpwifWfYHJ3.jpg" alt="Pickforu Stained Glass Puzzle Tree of Life Puzzles for Adults 1000 Pieces, Impossible Hard Difficult Challenging Puzzles for Adults, Colorful Mosaic Tree of Life Jigsaw Puzzle 1000 Pieces"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">1000 Piece Mosaic Tree of Life Jigsaw Puzzle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="44db7626-9ae7-11f1-a879-17f92a2c9501">            <a href="https://www.amazon.com/National-Landscape-Including-Yellowstone-Yosemite/dp/B0BFDDYZCW/" data-model-name="1000 Piece US National Parks Jigsaw Puzzle" 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/QWYxsDQubKcpSC832hznE3.jpg" alt="National Parks Puzzle for Adults 1000 Pieces, Travel Poster Landscape Puzzle Including Zion Yellowstone Yosemite, Nature Jigsaw Puzzles Scenery Mountain Scene"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">1000 Piece US National Parks Jigsaw Puzzle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="44db777a-9ae7-11f1-9ae3-9542e21f4918">            <a href="https://www.amazon.com/Rubber-Puzzle-Mat-Jigsaw-Pieces/dp/B0FSG189LJ/" data-model-name="Portable Roll-Up Jigsaw Puzzle Mat with 4 Sorting Trays" 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/q4BVeiKUxtsCrjHy8z5aJ3.jpg" alt="Rubber Puzzle Mat- Jigsaw Puzzle Mat Roll Up for 1000 and 1500 Pieces - Portable Mat With 4 Sorting Trays - Non-Slip Rubber Bottom Storage and Keeper - 46 X 26 Inches"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                                                                <div class="featured__title">Portable Roll-Up Jigsaw Puzzle Mat with 4 Sorting Trays</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div><h3 class="article-body__section" id="section-more-from-tom-s-guide"><span>More from Tom's Guide</span></h3><ul><li><a href="https://www.tomsguide.com/entertainment/movies/classic-movie-lovers-take-our-quiz-to-see-how-much-you-really-know-from-indiana-jones-to-star-wars">Classic movie lovers, take our quiz to see how much you really know — from ‘Indiana Jones’ to ‘Star Wars’</a></li><li><a href="https://www.tomsguide.com/gaming/toms-guide-jigsaw-puzzles-hardest-difficulty-has-me-stumped">Can you beat us at our own game? Tom’s Guide jigsaw puzzles are a great way to kill time between Wordles</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[ With World Photography Day just around the corner, I've found 18 affordable snappers that are worth ditching your phone for ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Camera lovers, rejoice! <a href="https://www.worldphotographyday.com/" target="_blank" rel="nofollow">World Photography Day</a> is this Wednesday, August 19, and it honours the art, craft, science and history of photography. </p><p>If we're being technical — the day <em>actually </em>celebrates the invention of the daguerreotype, which was the first practical and commercially successful photographic process developed in 1837. Since then, the world of photography has exploded, with photos (and videos) being a widely used medium of personal expression. </p><p>While everyone now has a decent camera in their hands, courtesy of their smartphone, opting for a dedicated device will give your shots better quality, depth, low-light performance, and hands-on controls for creative adjustments. Even flagship handsets with the <a href="https://www.tomsguide.com/us/best-phone-cameras,review-2272.html">best smartphone cameras</a> still pale in comparison to images taken by some of the <a href="https://www.tomsguide.com/cameras-photography/best-photos-i-ve-taken-on-cameras-reviewed-2025">best cameras</a>. </p><p>Even so, point-and-shoots and mini action cameras are consistently garnering popularity, with content creators and photography professionals using physical cameras and sharing their results on social media — rather than relying on the convenience of their smartphones. </p><p>Now, that's not to say you need to be a professional photographer or content creator to enjoy the day. As someone who loves a fun camera (or two), I'll be out and about on Wednesday, snapping all the best shots I can with my <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a>, and I'll even be partaking in <a href="https://www.eventbrite.com/e/fujifilm-celebrates-world-photography-day-with-a-colour-hunt-tickets-1996063073385?aff=oddtdtcreator&keep_tld=true" target="_blank" rel="nofollow">Fujifilm's Colour Hunt</a> this weekend in Sydney. </p><p>To get you in a snap-happy mood, I've gathered 18 beginner-friendly affordable cameras. These cams exist in the fun-but-not-serious photography category and come from brands like Kodak, Fujifilm, Insta360 and Polaroid. Combining a mix of instant prints, disposable film and digital snaps, the picks below come with Tom's Guide's tick of approval — and are all available on Amazon. Happy World Photography Day! 📸</p>        <div class="featured_product_block featured_block_hero" data-id="64e5dcf0-99dd-11f1-af44-7fcb4a0bbeb1">            <a href="https://www.amazon.com.au/Instax-Fujifilm-Instant-Camera-Candy/dp/B0G5P9T131/" data-model-name="Mini 13 Instant Camera, Candy Pink" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/vsTc73NmtFfwpHjnRdfJCC.jpg" alt="Instax Fujifilm Mini 13 Instant Camera, Candy Pink"></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">Mini 13 Instant Camera, Candy Pink</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5dd72-99dd-11f1-9763-a11243883b1f">            <a href="https://www.amazon.com.au/Polaroid-Instant-Camera-Colour-Photos/dp/B0FB3K6CN1/" data-model-name="Flip Instant Camera Black + Colour Film (16 Photos)" 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/BQoWb4YESJWrZMQTi4S4GN.jpg" alt="Polaroid Flip Pack Instant Camera Black + Colour Film (16 Photos)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Flip Instant Camera Black + Colour Film (16 Photos)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5dde0-99dd-11f1-8313-b1e92acd6d6d">            <a href="https://www.amazon.com.au/Insta360-Retro-Bundle-Special-White/dp/B0GSRQLQF2/" data-model-name="Go 3S Retro Bundle" 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/gTmtLNUoRKjxqEmTe5e6LC.jpg" alt="Insta360 Go 3s Retro Bundle Special Edition 64gb White - Small 4k Vlogging Camera, Hands-Free Pov Cam, Retro Filters, 4k30fps, Extra Bttery, Lightweight Wearable Vlog Camera for Street & Daily Shots"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Insta360</div>                                        <div class="featured__title">Go 3S Retro Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5de80-99dd-11f1-afa5-e15d36f23251">            <a href="https://www.amazon.com.au/Kodak-Ektar-Frame-Camera-Colour/dp/B0CBBVDNVF" data-model-name="Ektar H35N Half Frame 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/sTT7A7P4T9rHHjJtWz63mC.jpg" alt="Kodak Ektar H35n Half Frame Camera Colour: Glazed 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">Ektar H35N Half Frame Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5deee-99dd-11f1-b543-b1abf851c4ab">            <a href="https://www.amazon.com.au/Polaroid-Now-Generation-3-White/dp/B0DTTLVNL3" data-model-name="Now+ Generation 3 - White" 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/5vuu8nDFGqrS42AnDrzU6n.jpg" alt="Polaroid Now+ Generation 3 - White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Now+ Generation 3 - White</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5df66-99dd-11f1-b32f-01f3c18a10b3">            <a href="https://www.amazon.com.au/instax-EVO-Instant-Camera-Brown/dp/B0C34B6N1P" data-model-name="Mini Evo Instant Camera, Brown" 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/K8RsxMvytrY3eW2U7PvigC.jpg" alt="Instax Fujifilm Mini Evo Instant Camera, Brown"></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">Mini Evo Instant Camera, Brown</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e0ce-99dd-11f1-9937-397e91960273">            <a href="https://www.amazon.com.au/CHARMERA-Keychain-Digital-Camera-Collectable/dp/B0FKBDT8FR" data-model-name="Charmera Keychain Mini Digital Camera Blind 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/h9G7nyJBNmEygQRr3EyZYC.jpg" alt="Kodak Charmera Keychain Mini Digital Camera Collectable Blind Box Single Box (1pc)"></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 Keychain Mini Digital Camera Blind Box</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e222-99dd-11f1-8983-cd7066e111ee">            <a href="https://www.amazon.com.au/CHARMERA-Millennium-Keychain-Recording-Collectible/dp/B0H29JN7MF" data-model-name="Charmera Millennium Mini Camera Blind Box" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:76.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Qv9hvTnNZxMQSZuLzRvKRC.jpg" alt="New Kodak Charmera Millennium Mini Camera Blind Box | 1.6mp Keychain Digital Camera With Video Recording, Micro Sd Card Slot | Pocket Digicam Collectible (1pc)"></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 Millennium Mini Camera Blind Box</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e330-99dd-11f1-9051-39479a796e3b">            <a href="https://www.amazon.com.au/Insta360-Waterproof-Single-Lens-Stabilization-Touchscreen/dp/B0B9H572LC" data-model-name="X3 360° Action 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/JMXyc753oGFyxoJrmx3ZHC.jpg" alt="Insta360 X3 Waterproof 360° Action Camera With 1/2 Inch Sensor, 5.7k 360°, 72mp 360° Photos, Stabilization, 2.29 Inch Touch Screen, Vibration Feedback, Ai Editing, Live Streaming, Voice Control"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Insta360</div>                                        <div class="featured__title">X3 360° Action Camera </div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e452-99dd-11f1-98a5-4f80b8beebc7">            <a href="https://www.amazon.com.au/Polaroid-Go-Generation-White-Compatible/dp/B0GYQ93FJR/" data-model-name="Go Generation 3 - White" 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/wenBFfxPTvpBSTvdeoA2LD.jpg" alt="Polaroid Go Generation 3 - White (9191) - Only Compatible With Go Film"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Generation 3 - White</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e556-99dd-11f1-be10-9b0d36ea4bed">            <a href="https://www.amazon.com.au/Instax-Fujifilm-EVO-Instant-Camera/dp/B0FMYHKF7G/" data-model-name="Mini Evo Instant Camera, Gentle Rose" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:75.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/QjK24t7nopYcANswUnDtEC.jpg" alt="Instax Fujifilm Mini Evo Instant Camera, Gentle Rose"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>instax Fujifilm</div>                                        <div class="featured__title">Mini Evo Instant Camera, Gentle Rose</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e66e-99dd-11f1-bd5f-a5e62807be7d">            <a href="https://www.amazon.com.au/Kodak-Capture-memories-Exposures-8053415/dp/B002A3UKMS/" data-model-name="Single-Use Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:95.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kiRnPbSVJJQg4D247DS5XC.jpg" alt="Kodak Fun Saver Single Use Camera, Capture Your Memories! (27 Exposures)- 8053415, Red/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">Single-Use Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e790-99dd-11f1-94ba-bbbf0821cae6">            <a href="https://www.amazon.com.au/Instax-Fujifilm-Wide-Instant-Camera/dp/B0D5J4749K" data-model-name="Instax Wide 400 Instant Camera Green" 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/qzK8nH4GQbnKD6yneQJRbC.jpg" alt="Instax Fujifilm Wide 400 Instant Camera Green"></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">Instax Wide 400 Instant Camera Green</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e894-99dd-11f1-b640-07ebfb726498">            <a href="https://www.amazon.com.au/KODAK-FZ55-16-Megapixel-Stabiliser-Lithium-Ion/dp/B0B15LHF3K/" data-model-name="Pixpro FZ55 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/LDHwNDx4TaWqFZfEesuS8C.jpg" alt="Kodak Pixpro Fz55-16 Megapixel Digital Camera, 5x Optical Zoom, 2.7 Lcd, Optical Image Stabiliser, 720p Full Hd Video, Lithium-Ion, 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">Pixpro FZ55 Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e934-99dd-11f1-b1d2-4b3d9a67cf31">            <a href="https://www.amazon.com.au/Instax-Fujifilm-Link-Instant-Printer/dp/B0FNW5PZ3H/ref=sr_1_56" data-model-name="Mini Link 3 Instant Photo Printer + Super Mario 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/dVNU5BD9yuzEsManQHC6eC.jpg" alt="Instax Fujifilm Mini Link 3 Instant Photo Printer + Super Mario Case"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>instax Fujifilm</div>                                        <div class="featured__title">Mini Link 3 Instant Photo Printer + Super Mario Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e9b6-99dd-11f1-9470-fd39ef669ef3">            <a href="https://www.amazon.com.au/KODAK-Pixpro-C1-Ultra-Compact-Digital/dp/B0DY89Z83G" 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:86.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/WFCHt5RDNPcVJHguevpXNC.jpg" alt="Kodak Pixpro C1 – Ultra-Compact Digital Camera | 13mp Bsi Cmos, 4x Zoom, 2.8" Lcd Screen, Led Flash – Brown"></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="64e5ea42-99dd-11f1-9a33-0157d6015746">            <a href="https://www.amazon.com.au/Fujifilm-Quicksnap-Single-Use-Discontinued-Manufacturer/dp/B00012YIA0/" data-model-name="Quicksnap Flash 400 Disposable Camera 2 Pack" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:99.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/gYSqoWAjEkQdUwS9GaiNUC.jpg" alt="Fujifilm Quicksnap Flash 400 One-Time-Use Camera -2 Pack"></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 Camera 2 Pack</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5eac4-99dd-11f1-9b94-0b27a5f2f4a5">            <a href="https://www.amazon.com.au/Kodak-PRINTOMATIC-Digital-Instant-Sticky-Backed/dp/B075WXBGP6/" data-model-name="Printomatic Digital Instant Print Camera, Yellow" 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/Z4PA8GRhdDMoigyXkfiWfZ.jpg" alt="Kodak 5 Mp Printomatic Digital Instant Print Camera, Yellow"></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">Printomatic Digital Instant Print Camera, Yellow</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/with-world-photography-day-just-around-the-corner-ive-found-18-affordable-snappers-that-are-worth-ditching-your-phone-for</link>
                                                                            <description>
                            <![CDATA[ It's World Photography Day on August 19, and there's no better time to pick up one of these 18 affordable cameras to celebrate the occasion. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">4DkBQ8Y24pD4egWib3rQxK</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/qu63g5wboCb7CfxgPsFWYV-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 17 Aug 2026 05:10:37 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras & 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/qu63g5wboCb7CfxgPsFWYV-1280-80.jpg">
                                                            <media:credit><![CDATA[Future / Edited with Gemini]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[cameras from fujifilm, polaroid and insta360 on gradient background]]></media:description>                                                            <media:text><![CDATA[cameras from fujifilm, polaroid and insta360 on gradient background]]></media:text>
                                <media:title type="plain"><![CDATA[cameras from fujifilm, polaroid and insta360 on gradient background]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/qu63g5wboCb7CfxgPsFWYV-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Camera lovers, rejoice! <a href="https://www.worldphotographyday.com/" target="_blank" rel="nofollow">World Photography Day</a> is this Wednesday, August 19, and it honours the art, craft, science and history of photography. </p><p>If we're being technical — the day <em>actually </em>celebrates the invention of the daguerreotype, which was the first practical and commercially successful photographic process developed in 1837. Since then, the world of photography has exploded, with photos (and videos) being a widely used medium of personal expression. </p><p>While everyone now has a decent camera in their hands, courtesy of their smartphone, opting for a dedicated device will give your shots better quality, depth, low-light performance, and hands-on controls for creative adjustments. Even flagship handsets with the <a href="https://www.tomsguide.com/us/best-phone-cameras,review-2272.html">best smartphone cameras</a> still pale in comparison to images taken by some of the <a href="https://www.tomsguide.com/cameras-photography/best-photos-i-ve-taken-on-cameras-reviewed-2025">best cameras</a>. </p><p>Even so, point-and-shoots and mini action cameras are consistently garnering popularity, with content creators and photography professionals using physical cameras and sharing their results on social media — rather than relying on the convenience of their smartphones. </p><p>Now, that's not to say you need to be a professional photographer or content creator to enjoy the day. As someone who loves a fun camera (or two), I'll be out and about on Wednesday, snapping all the best shots I can with my <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a>, and I'll even be partaking in <a href="https://www.eventbrite.com/e/fujifilm-celebrates-world-photography-day-with-a-colour-hunt-tickets-1996063073385?aff=oddtdtcreator&keep_tld=true" target="_blank" rel="nofollow">Fujifilm's Colour Hunt</a> this weekend in Sydney. </p><p>To get you in a snap-happy mood, I've gathered 18 beginner-friendly affordable cameras. These cams exist in the fun-but-not-serious photography category and come from brands like Kodak, Fujifilm, Insta360 and Polaroid. Combining a mix of instant prints, disposable film and digital snaps, the picks below come with Tom's Guide's tick of approval — and are all available on Amazon. Happy World Photography Day! 📸</p>        <div class="featured_product_block featured_block_hero" data-id="64e5dcf0-99dd-11f1-af44-7fcb4a0bbeb1">            <a href="https://www.amazon.com.au/Instax-Fujifilm-Instant-Camera-Candy/dp/B0G5P9T131/" data-model-name="Mini 13 Instant Camera, Candy Pink" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/vsTc73NmtFfwpHjnRdfJCC.jpg" alt="Instax Fujifilm Mini 13 Instant Camera, Candy Pink"></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">Mini 13 Instant Camera, Candy Pink</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5dd72-99dd-11f1-9763-a11243883b1f">            <a href="https://www.amazon.com.au/Polaroid-Instant-Camera-Colour-Photos/dp/B0FB3K6CN1/" data-model-name="Flip Instant Camera Black + Colour Film (16 Photos)" 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/BQoWb4YESJWrZMQTi4S4GN.jpg" alt="Polaroid Flip Pack Instant Camera Black + Colour Film (16 Photos)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Flip Instant Camera Black + Colour Film (16 Photos)</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5dde0-99dd-11f1-8313-b1e92acd6d6d">            <a href="https://www.amazon.com.au/Insta360-Retro-Bundle-Special-White/dp/B0GSRQLQF2/" data-model-name="Go 3S Retro Bundle" 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/gTmtLNUoRKjxqEmTe5e6LC.jpg" alt="Insta360 Go 3s Retro Bundle Special Edition 64gb White - Small 4k Vlogging Camera, Hands-Free Pov Cam, Retro Filters, 4k30fps, Extra Bttery, Lightweight Wearable Vlog Camera for Street & Daily Shots"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Insta360</div>                                        <div class="featured__title">Go 3S Retro Bundle</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5de80-99dd-11f1-afa5-e15d36f23251">            <a href="https://www.amazon.com.au/Kodak-Ektar-Frame-Camera-Colour/dp/B0CBBVDNVF" data-model-name="Ektar H35N Half Frame 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/sTT7A7P4T9rHHjJtWz63mC.jpg" alt="Kodak Ektar H35n Half Frame Camera Colour: Glazed 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">Ektar H35N Half Frame Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5deee-99dd-11f1-b543-b1abf851c4ab">            <a href="https://www.amazon.com.au/Polaroid-Now-Generation-3-White/dp/B0DTTLVNL3" data-model-name="Now+ Generation 3 - White" 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/5vuu8nDFGqrS42AnDrzU6n.jpg" alt="Polaroid Now+ Generation 3 - White"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Now+ Generation 3 - White</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5df66-99dd-11f1-b32f-01f3c18a10b3">            <a href="https://www.amazon.com.au/instax-EVO-Instant-Camera-Brown/dp/B0C34B6N1P" data-model-name="Mini Evo Instant Camera, Brown" 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/K8RsxMvytrY3eW2U7PvigC.jpg" alt="Instax Fujifilm Mini Evo Instant Camera, Brown"></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">Mini Evo Instant Camera, Brown</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e0ce-99dd-11f1-9937-397e91960273">            <a href="https://www.amazon.com.au/CHARMERA-Keychain-Digital-Camera-Collectable/dp/B0FKBDT8FR" data-model-name="Charmera Keychain Mini Digital Camera Blind 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/h9G7nyJBNmEygQRr3EyZYC.jpg" alt="Kodak Charmera Keychain Mini Digital Camera Collectable Blind Box Single Box (1pc)"></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 Keychain Mini Digital Camera Blind Box</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e222-99dd-11f1-8983-cd7066e111ee">            <a href="https://www.amazon.com.au/CHARMERA-Millennium-Keychain-Recording-Collectible/dp/B0H29JN7MF" data-model-name="Charmera Millennium Mini Camera Blind Box" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:76.80%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Qv9hvTnNZxMQSZuLzRvKRC.jpg" alt="New Kodak Charmera Millennium Mini Camera Blind Box | 1.6mp Keychain Digital Camera With Video Recording, Micro Sd Card Slot | Pocket Digicam Collectible (1pc)"></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 Millennium Mini Camera Blind Box</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e330-99dd-11f1-9051-39479a796e3b">            <a href="https://www.amazon.com.au/Insta360-Waterproof-Single-Lens-Stabilization-Touchscreen/dp/B0B9H572LC" data-model-name="X3 360° Action 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/JMXyc753oGFyxoJrmx3ZHC.jpg" alt="Insta360 X3 Waterproof 360° Action Camera With 1/2 Inch Sensor, 5.7k 360°, 72mp 360° Photos, Stabilization, 2.29 Inch Touch Screen, Vibration Feedback, Ai Editing, Live Streaming, Voice Control"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Insta360</div>                                        <div class="featured__title">X3 360° Action Camera </div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e452-99dd-11f1-98a5-4f80b8beebc7">            <a href="https://www.amazon.com.au/Polaroid-Go-Generation-White-Compatible/dp/B0GYQ93FJR/" data-model-name="Go Generation 3 - White" 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/wenBFfxPTvpBSTvdeoA2LD.jpg" alt="Polaroid Go Generation 3 - White (9191) - Only Compatible With Go Film"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Polaroid</div>                                        <div class="featured__title">Go Generation 3 - White</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e556-99dd-11f1-be10-9b0d36ea4bed">            <a href="https://www.amazon.com.au/Instax-Fujifilm-EVO-Instant-Camera/dp/B0FMYHKF7G/" data-model-name="Mini Evo Instant Camera, Gentle Rose" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:75.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/QjK24t7nopYcANswUnDtEC.jpg" alt="Instax Fujifilm Mini Evo Instant Camera, Gentle Rose"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>instax Fujifilm</div>                                        <div class="featured__title">Mini Evo Instant Camera, Gentle Rose</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e66e-99dd-11f1-bd5f-a5e62807be7d">            <a href="https://www.amazon.com.au/Kodak-Capture-memories-Exposures-8053415/dp/B002A3UKMS/" data-model-name="Single-Use Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:95.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kiRnPbSVJJQg4D247DS5XC.jpg" alt="Kodak Fun Saver Single Use Camera, Capture Your Memories! (27 Exposures)- 8053415, Red/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">Single-Use Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e790-99dd-11f1-94ba-bbbf0821cae6">            <a href="https://www.amazon.com.au/Instax-Fujifilm-Wide-Instant-Camera/dp/B0D5J4749K" data-model-name="Instax Wide 400 Instant Camera Green" 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/qzK8nH4GQbnKD6yneQJRbC.jpg" alt="Instax Fujifilm Wide 400 Instant Camera Green"></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">Instax Wide 400 Instant Camera Green</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e894-99dd-11f1-b640-07ebfb726498">            <a href="https://www.amazon.com.au/KODAK-FZ55-16-Megapixel-Stabiliser-Lithium-Ion/dp/B0B15LHF3K/" data-model-name="Pixpro FZ55 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/LDHwNDx4TaWqFZfEesuS8C.jpg" alt="Kodak Pixpro Fz55-16 Megapixel Digital Camera, 5x Optical Zoom, 2.7 Lcd, Optical Image Stabiliser, 720p Full Hd Video, Lithium-Ion, 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">Pixpro FZ55 Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e934-99dd-11f1-b1d2-4b3d9a67cf31">            <a href="https://www.amazon.com.au/Instax-Fujifilm-Link-Instant-Printer/dp/B0FNW5PZ3H/ref=sr_1_56" data-model-name="Mini Link 3 Instant Photo Printer + Super Mario 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/dVNU5BD9yuzEsManQHC6eC.jpg" alt="Instax Fujifilm Mini Link 3 Instant Photo Printer + Super Mario Case"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>instax Fujifilm</div>                                        <div class="featured__title">Mini Link 3 Instant Photo Printer + Super Mario Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5e9b6-99dd-11f1-9470-fd39ef669ef3">            <a href="https://www.amazon.com.au/KODAK-Pixpro-C1-Ultra-Compact-Digital/dp/B0DY89Z83G" 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:86.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/WFCHt5RDNPcVJHguevpXNC.jpg" alt="Kodak Pixpro C1 – Ultra-Compact Digital Camera | 13mp Bsi Cmos, 4x Zoom, 2.8" Lcd Screen, Led Flash – Brown"></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="64e5ea42-99dd-11f1-9a33-0157d6015746">            <a href="https://www.amazon.com.au/Fujifilm-Quicksnap-Single-Use-Discontinued-Manufacturer/dp/B00012YIA0/" data-model-name="Quicksnap Flash 400 Disposable Camera 2 Pack" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:99.20%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/gYSqoWAjEkQdUwS9GaiNUC.jpg" alt="Fujifilm Quicksnap Flash 400 One-Time-Use Camera -2 Pack"></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 Camera 2 Pack</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="64e5eac4-99dd-11f1-9b94-0b27a5f2f4a5">            <a href="https://www.amazon.com.au/Kodak-PRINTOMATIC-Digital-Instant-Sticky-Backed/dp/B075WXBGP6/" data-model-name="Printomatic Digital Instant Print Camera, Yellow" 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/Z4PA8GRhdDMoigyXkfiWfZ.jpg" alt="Kodak 5 Mp Printomatic Digital Instant Print Camera, Yellow"></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">Printomatic Digital Instant Print Camera, Yellow</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I tested the new Insta360 X6 for a month to see if it's really worth $700 ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As I write this in August 2026, Insta360 just dropped the X6, and I spent the month before launch testing it. The X6 succeeds the fantastic <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-x5-review">Insta360 X5</a> — already the <a href="https://www.tomsguide.com/us/best-360-cameras,review-3737.html">best 360 camera</a> around for most people. </p><p>Indeed, for a long time, Insta360’s cameras (the 360 ones anyway) have been miles ahead of any rivals. For a while, they were the only 360 cameras capable of shooting 8K. Recently, though, rivals have closed the gap, with 8K cameras like the DJI Osmo 360 and GoPro MAX2. </p><p>To answer, Insta360 has made some fundamental updates to the X-line formula. The X6 gets larger sensors than the X5, a revamped processor, a boost in 8K frame rate, an OLED display, and built-in storage. All in a more compact, usable package than its predecessor, too.</p><p>So, is this the new champ? Find out in my full Insta360 X6 review.</p><h2 class="article-body__section" id="section-insta360-x6-review-specs"><span>Insta360 X6 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.com/Insta360-CINSABXA-Camera/dp/B0H5CR9LZF" target="_blank" rel="nofollow">$699</a> / <a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CR9LZF/" target="_blank" rel="nofollow">£619</a></p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>8K/50fps (360); 5K/60fps, 4K/100fps (Single lens)</p></td></tr><tr><td class="firstcol " ><p><strong>Sensors</strong></p></td><td  ><p>2x 1/1.1-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Yes.</p></td></tr><tr><td class="firstcol " ><p><strong>Screen</strong></p></td><td  ><p>2.32-inch OLED 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>Pre-recording</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless mic hookup</strong></p></td><td  ><p>Yes, via Bluetooth</p></td></tr><tr><td class="firstcol " ><p><strong>Wired mic hookup</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>66ft (20m)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery</strong></p></td><td  ><p>2,600 mAh</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>3.9 x 2 x 1.6 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>6.9 ounces</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-insta360-x6-review-price-availability"><span>Insta360 X6 review: Price & availability</span></h2><p>The Insta360 X6 comes in black or white and starts at <a href="https://www.amazon.com/Insta360-CINSABXA-Camera/dp/B0H5CR9LZF" target="_blank" rel="nofollow">$699</a> / <a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CR9LZF/" target="_blank" rel="nofollow">£619</a> for the standard bundle, which includes very basic accessories. I’d recommend the Essentials bundle, which costs <a href="https://www.amazon.com/Insta360-CINSABXA-Camera/dp/B0H5CJRJV7" target="_blank" rel="nofollow">$799</a> / <a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CJRJV7/" target="_blank" rel="nofollow">£709</a>. Insta360 always prices high, and the global memory crisis is probably partly to blame, but that’s still an astronomical price.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="eGgGjESeYzKmXmuyR97iHU" name="Insta360 X6--6" alt="A photo of the Insta360 X6 quick charge battery case" src="https://cdn.mos.cms.futurecdn.net/eGgGjESeYzKmXmuyR97iHU.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">The Essential Bundle comes with a dual battery charging case, among other accessories. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X5 and <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-360-review">DJI Osmo 360</a> launched $150 cheaper at $549, while the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/gopro-max2-review">GoPro MAX2</a> undercut everyone when it launched at $499. GoPro and DJI regularly discount their stuff, though, while the X5 hasn’t had noteworthy reductions to date. </p><p>Insta360 will keep selling the X5 at or near its original price (probably discounted more frequently), with the X6 positioned as the aspirational flagship upgrade. The thing is, the X5 is still better than anything else on the market save the X6. And given the X6’s crazy price, I’ll continue recommending the X5 to most people — while still pricey, it’s the best balance of price and performance.</p><h2 class="article-body__section" id="section-insta360-x6-review-design-controls"><span>Insta360 X6 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="SVcs4awtdWMza22aZ4EV4U" name="Insta360 X6--4" alt="A photo of the Insta360 X6 on a stone surface" src="https://cdn.mos.cms.futurecdn.net/SVcs4awtdWMza22aZ4EV4U.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Insta360 has made major improvements to the X6’s design. At 3.9 x 2 x 1.6 inches and 6.9 ounces, the X6 is shorter and a fraction lighter than the X5. This makes it easier to mount in smaller places and less drag-inducing at speed when helmet-mounted.</p><p>There’s now an OLED display, which is nicer-looking than the X5’s LED, more responsive and brighter. I did most of my testing in bright summer sunlight, but never had issues seeing the display. </p><p>Obviously, as an Insta360 product, the X6 feels extremely high quality and built-to-last. The same is also true of the X5, MAX2 and Osmo 360.</p><h3 id="controls">Controls</h3><p>Insta360 has tweaked the controls for the X6’s glove-wearing primary audience (motorcyclists, mountaineers, skiers, etc). The X5’s ‘Record’ button was very fiddly to press in gloves and while concentrating elsewhere — a potential safety issue. Insta360 has fixed this on the X6, moving it up from the lower rear to the side, and making it lighter to press. In the heat of the moment, it’s way easier to hit. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="xNPx53DMsCT5rLyMQayXuS" name="Insta360 X6--12" alt="A photo of the Insta360 X6 in hand with the record and power button on display" src="https://cdn.mos.cms.futurecdn.net/xNPx53DMsCT5rLyMQayXuS.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>There’s still a touch-screen ‘Record’ button where the X5’s physical button was, plus a ‘Full Screen Press’ setting (long-tapping anywhere on the screen will start recording). I found both even more frustrating than the old button. Full Screen Press constantly wanted more or less force and required way too much concentration to get right — again, dangerous at speed.</p><p>In-depth control happens in the menu. With all the new features added, Insta360’s menu is getting a little busy, so it might take a little getting used to. You can also control the camera via your smartphone, which is easier given the larger screen real estate, but not applicable to risky situations like motoring, skiing or flying.</p><h3 id="mounting">Mounting</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.15%;"><img id="ByFP7VhSKUMyxErK4ZFRKT" name="Insta360 X6--14" alt="A photo of the Insta360 X6 base, resting on a selfie stick" src="https://cdn.mos.cms.futurecdn.net/ByFP7VhSKUMyxErK4ZFRKT.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X6 mounts via a 1/4-inch thread — the default for action-footage accessories — giving you plenty of mounting options. There’s also a proprietary quick-release mounting system (adapter sold separately), which is useful for specific mounts requiring extra stability and quick, easy latching (like on a helmet).</p><h3 id="lenses">Lenses</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:1057px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="ojetExWVDCYPMXDub5U56U" name="Insta360 X6--5" alt="A photo of the Insta360 X6 in hand" src="https://cdn.mos.cms.futurecdn.net/v2/t:51,l:458,cw:1057,ch:595,q:80/ojetExWVDCYPMXDub5U56U.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Just like the X5, the X6 has interchangeable lenses. Insta360 has redesigned them to be more scratch-resistant, easier to replace and cheaper. Win, win, win!</p><p>Interchangeable lenses are fantastic. I’ve had more than one 360 lens damaged by flying stones from trucks. A few years ago, the only solution was lens covers, which affected sharpness. Now you needn’t choose between safety and image quality.</p><h3 id="waterproofing">Waterproofing</h3><p>Insta360 has boosted the X6’s waterproofing to 60 feet (20m) from the X5’s 49 feet (15m). That’s good news for underwater 360 shooters, giving an extra five meter buffer. The Osmo 360 is safe down to 33 feet (10m), while the MAX2 only manages 16 feet (5m).</p><h3 id="connectivity">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="4U3DtJoKgRCpcZzGGPuwjT" name="Insta360 X6--10" alt="A photo of the Insta360 X6 on a stone surface, one the screen are the internal storage and SD card storage displays" src="https://cdn.mos.cms.futurecdn.net/4U3DtJoKgRCpcZzGGPuwjT.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X6 features a USB-C port for charging, file transfer and wired accessories hookup — all standard on 360 cams. You can also hook up microphones wirelessly, like the <a href="https://www.tomsguide.com/audio/insta360-mic-air-review">Insta360 Mic Air</a> ($69), and pair wirelessly to the Insta360 app for camera control and file transfer. And for motorcyclists, the X6 pairs wirelessly with Sena, Cardo and other comms systems.</p><p>Insta360 has given the X6 internal storage (finally!!!), so you can still shoot if you fill or forget a MicroSD card. Transferring files from internal storage is also faster than using cards — important for large 8K files. DJI has done this for ages, so I appreciate Insta360 catching up, but the X6’s 47GB is under half the Osmo 360’s 105GB.</p><h2 class="article-body__section" id="section-insta360-x6-review-stabilization"><span>Insta360 X6 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/7datpqnM554" allowfullscreen></iframe></div></div><p>There’s little difference between Insta360’s, DJI’s and GoPro’s 360 stabilization. They’re all solid, and the X6 is no exception. 360 footage looks effortlessly smooth, as you can see in the 360 reframed video (in the video section).</p><p>I noticed some juddering in the X6’s single lens mode, as you can see above, but not always, as the footage directly below is nice and smooth with exactly the same settings. You can enable a Jitter Blur Reduction mode which I’d advise keeping on to avoid issues. </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/vOR4_7q75oA" allowfullscreen></iframe></div></div><p>Aside from the Jitter Blur mode, you can’t control stabilization on the X6 (unlike previous models and rivals). In single-lens mode, there’s a Horizon Lock setting, but that’s it. This isn’t really an issue for me, as I can’t think of a situation where I’d want anything less than maximum stabilization when using a 360 camera.</p><h2 class="article-body__section" id="section-insta360-x6-review-video"><span>Insta360 X6 review: Video</span></h2><p>Insta360 has given the X6 some major core upgrades versus the X5. Most importantly, there are two new, larger 1/1.1-inch square Sony sensors. Combined with a revised 4nm processor plus two dedicated video chips, the X6 is good for 13 stops of dynamic range, with cleaner images (i.e. less noisy) in low 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="5fyh2DyzhXXaPd9ceDR4mS" name="Insta360 X6--9" alt="A photo of the Insta360 X6 on a stone surface with the 8K resolution and 50fps frame rate being selected" src="https://cdn.mos.cms.futurecdn.net/5fyh2DyzhXXaPd9ceDR4mS.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The maximum frame rate at 8K has been boosted to 50fps (from 30fps on the X5), matching the Osmo 360. While not as smooth as 60fps, it’s nice to have something higher than 30fps for fast-paced content. YouTube supports 50fps uploads, although watching 50fps on a 60Hz display risks juddering issues as the two rates won’t always align perfectly.</p><p>8K/60fps would be a huge improvement, but is too technically difficult right now (hence no manufacturer managing it). At 8K, a 60fps frame rate is simply too much for today’s 360-cam processors to handle, given the unique demands they already have — processing two sensors, real-time stitching, encoding, managing memory bandwidth and thermal throttling etc. This is “not currently supported due to hardware limitations,” Insta360 told me, but you can bet it’s probably working on it.</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/ug7cmfZHp-Y" allowfullscreen></iframe></div></div><p>Anyway, footage from the X6 looks lovely. In the default profile, colors (especially blues and greens) pop in the sunlit footage above, looking vibrant and saturated but still natural. There are the usual range of in-camera color profiles, with more available in the app later on. The sensor and auto-metering handled the challenging light too, effortlessly adjusting exposure as the footage transitions from bright sunlight to shadowy woodland. The wide dynamic range was also on display here, with lots of detail in the shadows under trees, yet very little visible noise in darker areas.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6111px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="8gJxjBMbyqkRLAKD2ekzoG" name="Insta360 X6 - HDR Sample image.JPG" alt="A photo of a large three and three hot air balloons against a bright blue sky" src="https://cdn.mos.cms.futurecdn.net/8gJxjBMbyqkRLAKD2ekzoG.jpg" mos="" align="middle" fullscreen="1" width="6111" height="3437" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/8gJxjBMbyqkRLAKD2ekzoG.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 HDR still shot on the Insta360 X6. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>I noticed some minor lens artefacting, with very slight color fringing in high contrast areas (known as chromatic aberration). This was only visible when pixel peeping on still photos, though, and wasn’t noticeable in motion video. You can also see blurring in image corners, but this is down to the barrel distortion of those ultra-wide lenses, and to be expected.</p><h3 id="360-video">360 video</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/v1JZ7wbYbdc" allowfullscreen></iframe></div></div><p>The most important benefit of any 360 camera is the ability to shoot first, frame later. With a little practice in the app, you can edit the X6’s 360 footage so the frame is anywhere you want within the entire spherical capture. You choose precisely which camera angle you want at all times. In the footage below, the field of view (FoV) switches to the side to show off the beautiful landscape, then back to the road — you couldn’t do this with a fixed single-lens action camera.</p><p>360 capture also gives you the flexibility to get creative with crazy effects, like the Tiny Planet view, as you can see in the video below. In that video, you can also see the invisible selfie stick benefit (my hand appears empty, even though the camera was on a stick). This makes it look like the camera is floating.</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/Pdz2-njiSQM" allowfullscreen></iframe></div></div><p>As always with Insta360, the X6’s stitching is really good. 360 cameras work by taking the images from two sensors and lenses with +180-degree fields of view, then stitching together to form a 360-degree video. On the X6, stitching lines are barely noticeable in 4K output. I had to zoom in extremely closely to see the fuzzy-ish stitch, but there weren’t any jarring overlaps or jagged edges. </p><h3 id="single-lens">Single-lens</h3><p>You can also use the X6 in single-lens mode (now called “Flat mode”) at up to 60fps in 5K or up to 100fps in 4K. If you only need a single-lens camera, I would instead advise the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-6-review">DJI Osmo Action 6</a> ($426) — it’s designed specifically for that job and just better at it all round. </p><p>Still, it’s great to have both options on a 360 camera, which is why I love them. The X6’s single-lens mode is good enough for most uses, but you get the enormous versatility of 360 recording too.</p><h3 id="pro-features">Pro features</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.15%;"><img id="GiWirCLM3BMWbGPwrhWYLT" name="Insta360 X6--11" alt="A photo of the Insta360 X6 in hand the with menus being browsed" src="https://cdn.mos.cms.futurecdn.net/GiWirCLM3BMWbGPwrhWYLT.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X6 is primarily intended for content creators. You don’t get the same level of professionally-geared features as the GoPro MAX2. However, Insta360 has added a few handy bits, including I-Log for wide dynamic range capture, plus Dolby Vision and Dolby Atmos surround sound audio — the Dolby features could make the X6 a viable secondary camera for pros shooting in those spaces.</p><h3 id="low-light">Low light</h3><p>I’m thoroughly impressed by the low light performance of the two new sensors, in addition to the noise processing of the revised 4nm chip. As you can see in the footage below, low light video looks absolutely great. </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/oQNahSJmVUk" allowfullscreen></iframe></div></div><p>This was shot in the camera’s PureVideo low light mode at 8K/30fps. The X6 has boosted the exposure to brighten the scene (it was very dark in real life), revealing details in the sky and brightening up areas lit by the dim street light. There’s a little loss of sharpness, especially noticeable at the corners, from the smoothing effects of noise processing, but it’s nothing serious, and well worth the sacrifice given the minimal amount of visible noise in frame — especially in those super dark shadows.</p><h2 class="article-body__section" id="section-insta360-x6-review-audio"><span>Insta360 X6 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:1000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="ojetExWVDCYPMXDub5U56U" name="Insta360 X6--5" alt="A photo of the Insta360 X6 in hand" src="https://cdn.mos.cms.futurecdn.net/v2/t:401,l:491,cw:1000,ch:563,q:80/ojetExWVDCYPMXDub5U56U.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X6 features a similar mic array as the X5, including the built-in wind guard on the front. Insta360 has added lots of new features in the audio department. There’s now an “Auto Wind Reduction” mode which detects wind buffeting and automatically adjusts noise reduction. As always with Insta360’s wind reduction algos, it works really, really well. Performance in motorcycling footage (very windy) is on par with the high level of reduction on the X5. Check out the comparison video below to see the difference between Wind Reduction and Stereo (i.e. no reduction).</p><p>While I appreciate not having to flick between levels manually, I liked having the X5’s ‘low’ option there to manually select as a happy go-between. Wind Reduction thins out sound, so it’s nice to have a level of control over it, if circumstances demand. The X6's auto reduction seems to effectively always be on 'strong'. </p><p>The X6 now has an “Engine Sound Enhancement” mode, to keep focus on engine noise in motoring footage. Again, I think it works really well. The sound is a little muted, with much more of a focus on lower tones than footage shot only using Wind Reduction. Depending on the engine/exhaust you have, that may not deliver the sound you’re actually looking to capture. But hey, it works.</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/5t2l5z0ta2Q" allowfullscreen></iframe></div></div><p>The “Intelligent Audio Mode” adapts all audio settings based on the detected environmental sound. This is a super neat feature, as it’ll let you seamlessly switch between different styles of footage without needing manual changes to audio settings. It’ll turn on Engine Sound Enhancement by default, though, unless you ensure that mode is unticked.</p><p>For less noisy environments, the X6 has a “Vocal Priority” mode, and can hook up wirelessly to Insta360 and other <a href="https://www.tomsguide.com/best-picks/best-microphones#section-best-wireless"><u>Bluetooth mics</u></a>, making this an all-in-one vlogging solution.</p><h2 class="article-body__section" id="section-insta360-x6-review-app"><span>Insta360 X6 review: App</span></h2><p>The Insta360 app has long been the best smartphone app for 360 editing, packing the most features and the easiest straight-to-smartphone/social workflow. And at its core, it’s still just that — it’s the same app, with a strong roster of features, plenty of polish and the easiest 360-degree editor around. </p><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="nFWDxUxHvAJtz4zA9xrHXU" name="I360 App — X6" alt="Three screenshots showing footage being edited in the Insta360 app." src="https://cdn.mos.cms.futurecdn.net/nFWDxUxHvAJtz4zA9xrHXU.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/nFWDxUxHvAJtz4zA9xrHXU.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: Insta360 / Future)</span></figcaption></figure><p>But I’m a little confused by changes to the app. Insta360 has overcomplicated things a little — it’s like the manufacturer doesn’t know when to stop tinkering. Before you get into the main 360 timeline editor, there’s a new clip editor to edit individual clips before loading them into a main timeline. This is clunky and lacks most basic editing features. Moving into the full timeline editor afterwards, you can do everything that was in the preliminary editor, and all to individual clips on the timeline. So what’s the point in passing everything through a hamstrung editing layer beforehand?</p><p>Anyway, the app is still great, although if you're going to be doing lots of editing for YouTube, I'd advise you use the Insta360 Studio desktop app, which provides much greater control. You can also use the ReFrame plug-in available for Premiere. There's <strong>still</strong> no DaVinci Resolve plug-in confirmed though.</p><h2 class="article-body__section" id="section-insta360-x6-review-battery-overheating"><span>Insta360 X6 review: Battery & 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="fKW4455UFLTZiajdmQuufS" name="Insta360 X6--7" alt="A photo of the Insta360 X6 in hand with its battery compartment open" src="https://cdn.mos.cms.futurecdn.net/fKW4455UFLTZiajdmQuufS.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>I stress test all cameras for battery life and overheating indoors at 4K/60fps. The X6’s new 2,600mAh battery lasted for just under 117 minutes. That’s 30 minutes longer than the X5 (87 minutes) and 27 minutes longer than the MAX2 (90 minutes). It’s also five minutes longer than the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-x4-review-the-ultimate-360-camera">Insta360 X4</a> ($424) and Osmo 360 (both 112 minutes). Great performance.</p><p>The Insta360 X6 gets very hot to the touch at 4K/60fps and overheated after 33 minutes in warm ambient conditions (we had a heatwave here in Britain). In cooler conditions which aligned to my usual tests, it lasted 56 minutes. Better, but still worse than the X5, X4, Osmo 360 and MAX2, none of which overheated in the same test. That said, this is a stress test performed in fixed conditions for comparison purposes. On the move with the benefits of windchill, the X6 didn’t overheat once.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="VWXpJQGqz87h5vjeSm2A5U" name="Insta360 X6--8" alt="A photo of the Insta360 X6 in hand with the USB-C port showing" src="https://cdn.mos.cms.futurecdn.net/VWXpJQGqz87h5vjeSm2A5U.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>In 360 mode, at 8K/30fps, the X6 managed just under 155 minutes and didn’t overheat once, which is especially impressive, unmatched in fact. Recording at higher frame rates is where the true strain lies, though. At 50fps, it overheated after 25 minutes (and again every 20-25 minutes) but managed to record another impressive total of 90 minutes. Once again, outside of my stress test environment, windchill will dramatically reduce those overheating issues.</p><p>At their maximum 8K/30fps settings, the X5 managed 72 minutes while the MAX2 lasted 73 minutes — neither overheated. The Osmo 360, meanwhile, managed 112 minutes at 8K/30fps but only 30 minutes at 8K/50fps, both without overheating.</p><h2 class="article-body__section" id="section-insta360-x6-review-verdict"><span>Insta360 X6 review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2msciKTHcoxknD8W3gaSLU" name="Insta360 X6--3" alt="A photo of the Insta360 X6 on a stone surface, resting on a marble plinth" src="https://cdn.mos.cms.futurecdn.net/2msciKTHcoxknD8W3gaSLU.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Insta360 threw the kitchen sink at the X6, and I appreciate that. The manufacturer has pushed itself (as it always does) to better the technical performance of the X5, and succeeded at a core level. Imaging is better on the X6 than the X5, as is handling, battery life and waterproofing. </p><p>I’m not a fan of certain modes now being automated, as I feel this sacrifices creative control, but that’s not a huge issue. And while the X6 suffered more overheating than other 360 cameras in our lab tests, in reality that shouldn’t be a problem on the move.</p><p>I scored the Insta360 X5 our full five stars, and I stand by scoring it higher than the X6 today. The main problem is the X6’s price: a $699 start price is frankly crazy. Simply put, if you want the best balance of performance for money, the five-star X5 is still the better all-rounder. But if you want the best-of-the-best 360 camera from a technical standpoint, it has to be the X6. You’ll just need deep pockets. </p><h2 class="article-body__section" id="section-360-cameras-compared"><span>360 cameras compared</span></h2><div ><table><thead><tr><th class="firstcol " ><p><strong></strong></p></th><th  ><p><strong>Insta360 X6</strong></p></th><th  ><p><strong>DJI Osmo 360</strong></p></th><th  ><p><strong>GoPro MAX2</strong></p></th><th  ><p><strong>Insta360 X5</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Starting price</strong></p></td><td  ><p><a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CR9LZF/" target="_blank" rel="nofollow">£619</a> / <a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CR9LZF/" target="_blank" rel="nofollow">£619</a></p></td><td  ><p><a href="https://www.amazon.com/DJI-Adventure-Imaging-Extended-Batteries/dp/B0DTTZZ6Q3" target="_blank" rel="nofollow">$549</a> /<a href="https://www.amazon.co.uk/DJI-Standard-Imaging-Recording-Waterproof/dp/B0DTTZZ6Q3" target="_blank" rel="nofollow"> £409</a></p></td><td  ><p><a href="https://www.amazon.com/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FNGK6JZP" target="_blank" rel="nofollow">$499</a> / <a href="https://www.amazon.co.uk/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FP5SVBHK" target="_blank" rel="nofollow">£449</a> </p></td><td  ><p><a href="https://www.amazon.com/Insta360-Waterproof-Replaceable-Built-Stabilization/dp/B0DZCBYCNY/" target="_blank" rel="nofollow">$549</a> / <a href="https://www.amazon.co.uk/Insta360-X5-Standard-Bundle-Replaceable/dp/B0F5X156P4/" target="_blank" rel="nofollow">£519</a></p></td></tr><tr><td class="firstcol " ><p><strong>Max Res/fps</strong></p></td><td  ><p>8K/50fps</p></td><td  ><p>8K/50fps</p></td><td  ><p>8K/30fps</p></td><td  ><p>8K/30fps</p></td></tr><tr><td class="firstcol " ><p><strong>Sensors</strong></p></td><td  ><p>2x 1/1.1-inch</p></td><td  ><p>2x 1/1.1-inch</p></td><td  ><p>2x 1/2.3-inch</p></td><td  ><p>2x 1/2.8-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Touchscreen</strong></p></td><td  ><p>2.32-inch OLED</p></td><td  ><p>2-inch LED</p></td><td  ><p>1.82-inch LED</p></td><td  ><p>2.5-inch LED</p></td></tr><tr><td class="firstcol " ><p><strong>Replaceable lenses</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Internal memory</strong></p></td><td  ><p>47GB</p></td><td  ><p>106GB</p></td><td  ><p>None</p></td><td  ><p>None</p></td></tr><tr><td class="firstcol " ><p><strong>MicroSD slot</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Pre-recording</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless mic hookup</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</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><td  ><p>Bluetooth, Wi-Fi, USB-C</p></td><td  ><p>Bluetooth, Wi-Fi, USB-C</p></td><td  ><p>Bluetooth, Wi-Fi, USB-C</p></td></tr><tr><td class="firstcol " ><p><strong>Waterproofing</strong></p></td><td  ><p>66ft (20m)</p></td><td  ><p>33ft (10m)</p></td><td  ><p>16ft (5m)</p></td><td  ><p>49ft (15m)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery capacity</strong></p></td><td  ><p>2,600mAh</p></td><td  ><p>1,950mAh</p></td><td  ><p>1,960mAh</p></td><td  ><p>2,400mAh</p></td></tr><tr><td class="firstcol " ><p><strong>Tested 360 runtime (8K/30fps)</strong></p></td><td  ><p>155 mins</p></td><td  ><p>112 mins</p></td><td  ><p>73 mins</p></td><td  ><p>72 mins</p></td></tr><tr><td class="firstcol " ><p><strong>Tested single-lens runtime (4K/60fps)</strong></p></td><td  ><p>117 mins</p></td><td  ><p>112 mins</p></td><td  ><p>90 mins</p></td><td  ><p>87 mins</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>3.9 x 2 x 1.6 inches</p></td><td  ><p>2.4 x 1.4 x 3.1 inches</p></td><td  ><p>2.5 x 2.7 x 1.9 inches</p></td><td  ><p>4.9 x 1.8 x 1.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>6.9 ounces</p></td><td  ><p>6.4 ounces</p></td><td  ><p>6.8 ounces</p></td><td  ><p>7.05 ounces</p></td></tr></tbody></table></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-x6-review</link>
                                                                            <description>
                            <![CDATA[ The Insta360 X6 is the latest flagship 360 camera from the best in the business. It’s a major improvement on the fantastic X5, but that comes at a serious price. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">Rv7k4miJP9faKGbNrz8V3o</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/fezYWNa9qjSdw9owFKrKLU-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Thu, 13 Aug 2026 10:19:49 +0000</pubDate>                                                                                                                                <updated>Thu, 13 Aug 2026 10:24:39 +0000</updated>
                                                                                                                                            <category><![CDATA[GoPros & Action Cameras]]></category>
                                                    <category><![CDATA[Cameras & 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/fezYWNa9qjSdw9owFKrKLU-1280-80.jpg">
                                                            <media:credit><![CDATA[Peter Wolinski / Future]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A photo of the Insta360 X6 on a stone surface, leaning on a marble plinth]]></media:description>                                                            <media:text><![CDATA[A photo of the Insta360 X6 on a stone surface, leaning on a marble plinth]]></media:text>
                                <media:title type="plain"><![CDATA[A photo of the Insta360 X6 on a stone surface, leaning on a marble plinth]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/fezYWNa9qjSdw9owFKrKLU-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As I write this in August 2026, Insta360 just dropped the X6, and I spent the month before launch testing it. The X6 succeeds the fantastic <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-x5-review">Insta360 X5</a> — already the <a href="https://www.tomsguide.com/us/best-360-cameras,review-3737.html">best 360 camera</a> around for most people. </p><p>Indeed, for a long time, Insta360’s cameras (the 360 ones anyway) have been miles ahead of any rivals. For a while, they were the only 360 cameras capable of shooting 8K. Recently, though, rivals have closed the gap, with 8K cameras like the DJI Osmo 360 and GoPro MAX2. </p><p>To answer, Insta360 has made some fundamental updates to the X-line formula. The X6 gets larger sensors than the X5, a revamped processor, a boost in 8K frame rate, an OLED display, and built-in storage. All in a more compact, usable package than its predecessor, too.</p><p>So, is this the new champ? Find out in my full Insta360 X6 review.</p><h2 class="article-body__section" id="section-insta360-x6-review-specs"><span>Insta360 X6 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.com/Insta360-CINSABXA-Camera/dp/B0H5CR9LZF" target="_blank" rel="nofollow">$699</a> / <a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CR9LZF/" target="_blank" rel="nofollow">£619</a></p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>8K/50fps (360); 5K/60fps, 4K/100fps (Single lens)</p></td></tr><tr><td class="firstcol " ><p><strong>Sensors</strong></p></td><td  ><p>2x 1/1.1-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Yes.</p></td></tr><tr><td class="firstcol " ><p><strong>Screen</strong></p></td><td  ><p>2.32-inch OLED 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>Pre-recording</strong></p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless mic hookup</strong></p></td><td  ><p>Yes, via Bluetooth</p></td></tr><tr><td class="firstcol " ><p><strong>Wired mic hookup</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>66ft (20m)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery</strong></p></td><td  ><p>2,600 mAh</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>3.9 x 2 x 1.6 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>6.9 ounces</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-insta360-x6-review-price-availability"><span>Insta360 X6 review: Price & availability</span></h2><p>The Insta360 X6 comes in black or white and starts at <a href="https://www.amazon.com/Insta360-CINSABXA-Camera/dp/B0H5CR9LZF" target="_blank" rel="nofollow">$699</a> / <a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CR9LZF/" target="_blank" rel="nofollow">£619</a> for the standard bundle, which includes very basic accessories. I’d recommend the Essentials bundle, which costs <a href="https://www.amazon.com/Insta360-CINSABXA-Camera/dp/B0H5CJRJV7" target="_blank" rel="nofollow">$799</a> / <a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CJRJV7/" target="_blank" rel="nofollow">£709</a>. Insta360 always prices high, and the global memory crisis is probably partly to blame, but that’s still an astronomical price.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="eGgGjESeYzKmXmuyR97iHU" name="Insta360 X6--6" alt="A photo of the Insta360 X6 quick charge battery case" src="https://cdn.mos.cms.futurecdn.net/eGgGjESeYzKmXmuyR97iHU.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="caption-text">The Essential Bundle comes with a dual battery charging case, among other accessories. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X5 and <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-360-review">DJI Osmo 360</a> launched $150 cheaper at $549, while the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/gopro-max2-review">GoPro MAX2</a> undercut everyone when it launched at $499. GoPro and DJI regularly discount their stuff, though, while the X5 hasn’t had noteworthy reductions to date. </p><p>Insta360 will keep selling the X5 at or near its original price (probably discounted more frequently), with the X6 positioned as the aspirational flagship upgrade. The thing is, the X5 is still better than anything else on the market save the X6. And given the X6’s crazy price, I’ll continue recommending the X5 to most people — while still pricey, it’s the best balance of price and performance.</p><h2 class="article-body__section" id="section-insta360-x6-review-design-controls"><span>Insta360 X6 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="SVcs4awtdWMza22aZ4EV4U" name="Insta360 X6--4" alt="A photo of the Insta360 X6 on a stone surface" src="https://cdn.mos.cms.futurecdn.net/SVcs4awtdWMza22aZ4EV4U.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Insta360 has made major improvements to the X6’s design. At 3.9 x 2 x 1.6 inches and 6.9 ounces, the X6 is shorter and a fraction lighter than the X5. This makes it easier to mount in smaller places and less drag-inducing at speed when helmet-mounted.</p><p>There’s now an OLED display, which is nicer-looking than the X5’s LED, more responsive and brighter. I did most of my testing in bright summer sunlight, but never had issues seeing the display. </p><p>Obviously, as an Insta360 product, the X6 feels extremely high quality and built-to-last. The same is also true of the X5, MAX2 and Osmo 360.</p><h3 id="controls">Controls</h3><p>Insta360 has tweaked the controls for the X6’s glove-wearing primary audience (motorcyclists, mountaineers, skiers, etc). The X5’s ‘Record’ button was very fiddly to press in gloves and while concentrating elsewhere — a potential safety issue. Insta360 has fixed this on the X6, moving it up from the lower rear to the side, and making it lighter to press. In the heat of the moment, it’s way easier to hit. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="xNPx53DMsCT5rLyMQayXuS" name="Insta360 X6--12" alt="A photo of the Insta360 X6 in hand with the record and power button on display" src="https://cdn.mos.cms.futurecdn.net/xNPx53DMsCT5rLyMQayXuS.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>There’s still a touch-screen ‘Record’ button where the X5’s physical button was, plus a ‘Full Screen Press’ setting (long-tapping anywhere on the screen will start recording). I found both even more frustrating than the old button. Full Screen Press constantly wanted more or less force and required way too much concentration to get right — again, dangerous at speed.</p><p>In-depth control happens in the menu. With all the new features added, Insta360’s menu is getting a little busy, so it might take a little getting used to. You can also control the camera via your smartphone, which is easier given the larger screen real estate, but not applicable to risky situations like motoring, skiing or flying.</p><h3 id="mounting">Mounting</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.15%;"><img id="ByFP7VhSKUMyxErK4ZFRKT" name="Insta360 X6--14" alt="A photo of the Insta360 X6 base, resting on a selfie stick" src="https://cdn.mos.cms.futurecdn.net/ByFP7VhSKUMyxErK4ZFRKT.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X6 mounts via a 1/4-inch thread — the default for action-footage accessories — giving you plenty of mounting options. There’s also a proprietary quick-release mounting system (adapter sold separately), which is useful for specific mounts requiring extra stability and quick, easy latching (like on a helmet).</p><h3 id="lenses">Lenses</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:1057px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="ojetExWVDCYPMXDub5U56U" name="Insta360 X6--5" alt="A photo of the Insta360 X6 in hand" src="https://cdn.mos.cms.futurecdn.net/v2/t:51,l:458,cw:1057,ch:595,q:80/ojetExWVDCYPMXDub5U56U.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Just like the X5, the X6 has interchangeable lenses. Insta360 has redesigned them to be more scratch-resistant, easier to replace and cheaper. Win, win, win!</p><p>Interchangeable lenses are fantastic. I’ve had more than one 360 lens damaged by flying stones from trucks. A few years ago, the only solution was lens covers, which affected sharpness. Now you needn’t choose between safety and image quality.</p><h3 id="waterproofing">Waterproofing</h3><p>Insta360 has boosted the X6’s waterproofing to 60 feet (20m) from the X5’s 49 feet (15m). That’s good news for underwater 360 shooters, giving an extra five meter buffer. The Osmo 360 is safe down to 33 feet (10m), while the MAX2 only manages 16 feet (5m).</p><h3 id="connectivity">Connectivity</h3><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="4U3DtJoKgRCpcZzGGPuwjT" name="Insta360 X6--10" alt="A photo of the Insta360 X6 on a stone surface, one the screen are the internal storage and SD card storage displays" src="https://cdn.mos.cms.futurecdn.net/4U3DtJoKgRCpcZzGGPuwjT.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X6 features a USB-C port for charging, file transfer and wired accessories hookup — all standard on 360 cams. You can also hook up microphones wirelessly, like the <a href="https://www.tomsguide.com/audio/insta360-mic-air-review">Insta360 Mic Air</a> ($69), and pair wirelessly to the Insta360 app for camera control and file transfer. And for motorcyclists, the X6 pairs wirelessly with Sena, Cardo and other comms systems.</p><p>Insta360 has given the X6 internal storage (finally!!!), so you can still shoot if you fill or forget a MicroSD card. Transferring files from internal storage is also faster than using cards — important for large 8K files. DJI has done this for ages, so I appreciate Insta360 catching up, but the X6’s 47GB is under half the Osmo 360’s 105GB.</p><h2 class="article-body__section" id="section-insta360-x6-review-stabilization"><span>Insta360 X6 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/7datpqnM554" allowfullscreen></iframe></div></div><p>There’s little difference between Insta360’s, DJI’s and GoPro’s 360 stabilization. They’re all solid, and the X6 is no exception. 360 footage looks effortlessly smooth, as you can see in the 360 reframed video (in the video section).</p><p>I noticed some juddering in the X6’s single lens mode, as you can see above, but not always, as the footage directly below is nice and smooth with exactly the same settings. You can enable a Jitter Blur Reduction mode which I’d advise keeping on to avoid issues. </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/vOR4_7q75oA" allowfullscreen></iframe></div></div><p>Aside from the Jitter Blur mode, you can’t control stabilization on the X6 (unlike previous models and rivals). In single-lens mode, there’s a Horizon Lock setting, but that’s it. This isn’t really an issue for me, as I can’t think of a situation where I’d want anything less than maximum stabilization when using a 360 camera.</p><h2 class="article-body__section" id="section-insta360-x6-review-video"><span>Insta360 X6 review: Video</span></h2><p>Insta360 has given the X6 some major core upgrades versus the X5. Most importantly, there are two new, larger 1/1.1-inch square Sony sensors. Combined with a revised 4nm processor plus two dedicated video chips, the X6 is good for 13 stops of dynamic range, with cleaner images (i.e. less noisy) in low 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="5fyh2DyzhXXaPd9ceDR4mS" name="Insta360 X6--9" alt="A photo of the Insta360 X6 on a stone surface with the 8K resolution and 50fps frame rate being selected" src="https://cdn.mos.cms.futurecdn.net/5fyh2DyzhXXaPd9ceDR4mS.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The maximum frame rate at 8K has been boosted to 50fps (from 30fps on the X5), matching the Osmo 360. While not as smooth as 60fps, it’s nice to have something higher than 30fps for fast-paced content. YouTube supports 50fps uploads, although watching 50fps on a 60Hz display risks juddering issues as the two rates won’t always align perfectly.</p><p>8K/60fps would be a huge improvement, but is too technically difficult right now (hence no manufacturer managing it). At 8K, a 60fps frame rate is simply too much for today’s 360-cam processors to handle, given the unique demands they already have — processing two sensors, real-time stitching, encoding, managing memory bandwidth and thermal throttling etc. This is “not currently supported due to hardware limitations,” Insta360 told me, but you can bet it’s probably working on it.</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/ug7cmfZHp-Y" allowfullscreen></iframe></div></div><p>Anyway, footage from the X6 looks lovely. In the default profile, colors (especially blues and greens) pop in the sunlit footage above, looking vibrant and saturated but still natural. There are the usual range of in-camera color profiles, with more available in the app later on. The sensor and auto-metering handled the challenging light too, effortlessly adjusting exposure as the footage transitions from bright sunlight to shadowy woodland. The wide dynamic range was also on display here, with lots of detail in the shadows under trees, yet very little visible noise in darker areas.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6111px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="8gJxjBMbyqkRLAKD2ekzoG" name="Insta360 X6 - HDR Sample image.JPG" alt="A photo of a large three and three hot air balloons against a bright blue sky" src="https://cdn.mos.cms.futurecdn.net/8gJxjBMbyqkRLAKD2ekzoG.jpg" mos="" align="middle" fullscreen="1" width="6111" height="3437" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/8gJxjBMbyqkRLAKD2ekzoG.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 HDR still shot on the Insta360 X6. </span><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>I noticed some minor lens artefacting, with very slight color fringing in high contrast areas (known as chromatic aberration). This was only visible when pixel peeping on still photos, though, and wasn’t noticeable in motion video. You can also see blurring in image corners, but this is down to the barrel distortion of those ultra-wide lenses, and to be expected.</p><h3 id="360-video">360 video</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/v1JZ7wbYbdc" allowfullscreen></iframe></div></div><p>The most important benefit of any 360 camera is the ability to shoot first, frame later. With a little practice in the app, you can edit the X6’s 360 footage so the frame is anywhere you want within the entire spherical capture. You choose precisely which camera angle you want at all times. In the footage below, the field of view (FoV) switches to the side to show off the beautiful landscape, then back to the road — you couldn’t do this with a fixed single-lens action camera.</p><p>360 capture also gives you the flexibility to get creative with crazy effects, like the Tiny Planet view, as you can see in the video below. In that video, you can also see the invisible selfie stick benefit (my hand appears empty, even though the camera was on a stick). This makes it look like the camera is floating.</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/Pdz2-njiSQM" allowfullscreen></iframe></div></div><p>As always with Insta360, the X6’s stitching is really good. 360 cameras work by taking the images from two sensors and lenses with +180-degree fields of view, then stitching together to form a 360-degree video. On the X6, stitching lines are barely noticeable in 4K output. I had to zoom in extremely closely to see the fuzzy-ish stitch, but there weren’t any jarring overlaps or jagged edges. </p><h3 id="single-lens">Single-lens</h3><p>You can also use the X6 in single-lens mode (now called “Flat mode”) at up to 60fps in 5K or up to 100fps in 4K. If you only need a single-lens camera, I would instead advise the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/dji-osmo-action-6-review">DJI Osmo Action 6</a> ($426) — it’s designed specifically for that job and just better at it all round. </p><p>Still, it’s great to have both options on a 360 camera, which is why I love them. The X6’s single-lens mode is good enough for most uses, but you get the enormous versatility of 360 recording too.</p><h3 id="pro-features">Pro features</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.15%;"><img id="GiWirCLM3BMWbGPwrhWYLT" name="Insta360 X6--11" alt="A photo of the Insta360 X6 in hand the with menus being browsed" src="https://cdn.mos.cms.futurecdn.net/GiWirCLM3BMWbGPwrhWYLT.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X6 is primarily intended for content creators. You don’t get the same level of professionally-geared features as the GoPro MAX2. However, Insta360 has added a few handy bits, including I-Log for wide dynamic range capture, plus Dolby Vision and Dolby Atmos surround sound audio — the Dolby features could make the X6 a viable secondary camera for pros shooting in those spaces.</p><h3 id="low-light">Low light</h3><p>I’m thoroughly impressed by the low light performance of the two new sensors, in addition to the noise processing of the revised 4nm chip. As you can see in the footage below, low light video looks absolutely great. </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/oQNahSJmVUk" allowfullscreen></iframe></div></div><p>This was shot in the camera’s PureVideo low light mode at 8K/30fps. The X6 has boosted the exposure to brighten the scene (it was very dark in real life), revealing details in the sky and brightening up areas lit by the dim street light. There’s a little loss of sharpness, especially noticeable at the corners, from the smoothing effects of noise processing, but it’s nothing serious, and well worth the sacrifice given the minimal amount of visible noise in frame — especially in those super dark shadows.</p><h2 class="article-body__section" id="section-insta360-x6-review-audio"><span>Insta360 X6 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:1000px;"><p class="vanilla-image-block" style="padding-top:56.30%;"><img id="ojetExWVDCYPMXDub5U56U" name="Insta360 X6--5" alt="A photo of the Insta360 X6 in hand" src="https://cdn.mos.cms.futurecdn.net/v2/t:401,l:491,cw:1000,ch:563,q:80/ojetExWVDCYPMXDub5U56U.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>The X6 features a similar mic array as the X5, including the built-in wind guard on the front. Insta360 has added lots of new features in the audio department. There’s now an “Auto Wind Reduction” mode which detects wind buffeting and automatically adjusts noise reduction. As always with Insta360’s wind reduction algos, it works really, really well. Performance in motorcycling footage (very windy) is on par with the high level of reduction on the X5. Check out the comparison video below to see the difference between Wind Reduction and Stereo (i.e. no reduction).</p><p>While I appreciate not having to flick between levels manually, I liked having the X5’s ‘low’ option there to manually select as a happy go-between. Wind Reduction thins out sound, so it’s nice to have a level of control over it, if circumstances demand. The X6's auto reduction seems to effectively always be on 'strong'. </p><p>The X6 now has an “Engine Sound Enhancement” mode, to keep focus on engine noise in motoring footage. Again, I think it works really well. The sound is a little muted, with much more of a focus on lower tones than footage shot only using Wind Reduction. Depending on the engine/exhaust you have, that may not deliver the sound you’re actually looking to capture. But hey, it works.</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/5t2l5z0ta2Q" allowfullscreen></iframe></div></div><p>The “Intelligent Audio Mode” adapts all audio settings based on the detected environmental sound. This is a super neat feature, as it’ll let you seamlessly switch between different styles of footage without needing manual changes to audio settings. It’ll turn on Engine Sound Enhancement by default, though, unless you ensure that mode is unticked.</p><p>For less noisy environments, the X6 has a “Vocal Priority” mode, and can hook up wirelessly to Insta360 and other <a href="https://www.tomsguide.com/best-picks/best-microphones#section-best-wireless"><u>Bluetooth mics</u></a>, making this an all-in-one vlogging solution.</p><h2 class="article-body__section" id="section-insta360-x6-review-app"><span>Insta360 X6 review: App</span></h2><p>The Insta360 app has long been the best smartphone app for 360 editing, packing the most features and the easiest straight-to-smartphone/social workflow. And at its core, it’s still just that — it’s the same app, with a strong roster of features, plenty of polish and the easiest 360-degree editor around. </p><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="nFWDxUxHvAJtz4zA9xrHXU" name="I360 App — X6" alt="Three screenshots showing footage being edited in the Insta360 app." src="https://cdn.mos.cms.futurecdn.net/nFWDxUxHvAJtz4zA9xrHXU.jpg" mos="" align="middle" fullscreen="1" width="5333" height="3000" attribution="" endorsement="" class="inline expandable"><a href='https://cdn.mos.cms.futurecdn.net/nFWDxUxHvAJtz4zA9xrHXU.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: Insta360 / Future)</span></figcaption></figure><p>But I’m a little confused by changes to the app. Insta360 has overcomplicated things a little — it’s like the manufacturer doesn’t know when to stop tinkering. Before you get into the main 360 timeline editor, there’s a new clip editor to edit individual clips before loading them into a main timeline. This is clunky and lacks most basic editing features. Moving into the full timeline editor afterwards, you can do everything that was in the preliminary editor, and all to individual clips on the timeline. So what’s the point in passing everything through a hamstrung editing layer beforehand?</p><p>Anyway, the app is still great, although if you're going to be doing lots of editing for YouTube, I'd advise you use the Insta360 Studio desktop app, which provides much greater control. You can also use the ReFrame plug-in available for Premiere. There's <strong>still</strong> no DaVinci Resolve plug-in confirmed though.</p><h2 class="article-body__section" id="section-insta360-x6-review-battery-overheating"><span>Insta360 X6 review: Battery & 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:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="fKW4455UFLTZiajdmQuufS" name="Insta360 X6--7" alt="A photo of the Insta360 X6 in hand with its battery compartment open" src="https://cdn.mos.cms.futurecdn.net/fKW4455UFLTZiajdmQuufS.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>I stress test all cameras for battery life and overheating indoors at 4K/60fps. The X6’s new 2,600mAh battery lasted for just under 117 minutes. That’s 30 minutes longer than the X5 (87 minutes) and 27 minutes longer than the MAX2 (90 minutes). It’s also five minutes longer than the <a href="https://www.tomsguide.com/cameras-photography/gopro-action-cameras/insta360-x4-review-the-ultimate-360-camera">Insta360 X4</a> ($424) and Osmo 360 (both 112 minutes). Great performance.</p><p>The Insta360 X6 gets very hot to the touch at 4K/60fps and overheated after 33 minutes in warm ambient conditions (we had a heatwave here in Britain). In cooler conditions which aligned to my usual tests, it lasted 56 minutes. Better, but still worse than the X5, X4, Osmo 360 and MAX2, none of which overheated in the same test. That said, this is a stress test performed in fixed conditions for comparison purposes. On the move with the benefits of windchill, the X6 didn’t overheat once.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.15%;"><img id="VWXpJQGqz87h5vjeSm2A5U" name="Insta360 X6--8" alt="A photo of the Insta360 X6 in hand with the USB-C port showing" src="https://cdn.mos.cms.futurecdn.net/VWXpJQGqz87h5vjeSm2A5U.jpg" mos="" align="middle" fullscreen="" width="2000" height="1123" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>In 360 mode, at 8K/30fps, the X6 managed just under 155 minutes and didn’t overheat once, which is especially impressive, unmatched in fact. Recording at higher frame rates is where the true strain lies, though. At 50fps, it overheated after 25 minutes (and again every 20-25 minutes) but managed to record another impressive total of 90 minutes. Once again, outside of my stress test environment, windchill will dramatically reduce those overheating issues.</p><p>At their maximum 8K/30fps settings, the X5 managed 72 minutes while the MAX2 lasted 73 minutes — neither overheated. The Osmo 360, meanwhile, managed 112 minutes at 8K/30fps but only 30 minutes at 8K/50fps, both without overheating.</p><h2 class="article-body__section" id="section-insta360-x6-review-verdict"><span>Insta360 X6 review: Verdict</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="2msciKTHcoxknD8W3gaSLU" name="Insta360 X6--3" alt="A photo of the Insta360 X6 on a stone surface, resting on a marble plinth" src="https://cdn.mos.cms.futurecdn.net/2msciKTHcoxknD8W3gaSLU.jpg" mos="" align="middle" fullscreen="" width="2000" height="1125" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Peter Wolinski / Future)</span></figcaption></figure><p>Insta360 threw the kitchen sink at the X6, and I appreciate that. The manufacturer has pushed itself (as it always does) to better the technical performance of the X5, and succeeded at a core level. Imaging is better on the X6 than the X5, as is handling, battery life and waterproofing. </p><p>I’m not a fan of certain modes now being automated, as I feel this sacrifices creative control, but that’s not a huge issue. And while the X6 suffered more overheating than other 360 cameras in our lab tests, in reality that shouldn’t be a problem on the move.</p><p>I scored the Insta360 X5 our full five stars, and I stand by scoring it higher than the X6 today. The main problem is the X6’s price: a $699 start price is frankly crazy. Simply put, if you want the best balance of performance for money, the five-star X5 is still the better all-rounder. But if you want the best-of-the-best 360 camera from a technical standpoint, it has to be the X6. You’ll just need deep pockets. </p><h2 class="article-body__section" id="section-360-cameras-compared"><span>360 cameras compared</span></h2><div ><table><thead><tr><th class="firstcol " ><p><strong></strong></p></th><th  ><p><strong>Insta360 X6</strong></p></th><th  ><p><strong>DJI Osmo 360</strong></p></th><th  ><p><strong>GoPro MAX2</strong></p></th><th  ><p><strong>Insta360 X5</strong></p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Starting price</strong></p></td><td  ><p><a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CR9LZF/" target="_blank" rel="nofollow">£619</a> / <a href="https://www.amazon.co.uk/Insta360-CINSABXA-Action-Camera/dp/B0H5CR9LZF/" target="_blank" rel="nofollow">£619</a></p></td><td  ><p><a href="https://www.amazon.com/DJI-Adventure-Imaging-Extended-Batteries/dp/B0DTTZZ6Q3" target="_blank" rel="nofollow">$549</a> /<a href="https://www.amazon.co.uk/DJI-Standard-Imaging-Recording-Waterproof/dp/B0DTTZZ6Q3" target="_blank" rel="nofollow"> £409</a></p></td><td  ><p><a href="https://www.amazon.com/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FNGK6JZP" target="_blank" rel="nofollow">$499</a> / <a href="https://www.amazon.co.uk/GoPro-MAX2-Traditional-Replaceable-Stabilization/dp/B0FP5SVBHK" target="_blank" rel="nofollow">£449</a> </p></td><td  ><p><a href="https://www.amazon.com/Insta360-Waterproof-Replaceable-Built-Stabilization/dp/B0DZCBYCNY/" target="_blank" rel="nofollow">$549</a> / <a href="https://www.amazon.co.uk/Insta360-X5-Standard-Bundle-Replaceable/dp/B0F5X156P4/" target="_blank" rel="nofollow">£519</a></p></td></tr><tr><td class="firstcol " ><p><strong>Max Res/fps</strong></p></td><td  ><p>8K/50fps</p></td><td  ><p>8K/50fps</p></td><td  ><p>8K/30fps</p></td><td  ><p>8K/30fps</p></td></tr><tr><td class="firstcol " ><p><strong>Sensors</strong></p></td><td  ><p>2x 1/1.1-inch</p></td><td  ><p>2x 1/1.1-inch</p></td><td  ><p>2x 1/2.3-inch</p></td><td  ><p>2x 1/2.8-inch</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Touchscreen</strong></p></td><td  ><p>2.32-inch OLED</p></td><td  ><p>2-inch LED</p></td><td  ><p>1.82-inch LED</p></td><td  ><p>2.5-inch LED</p></td></tr><tr><td class="firstcol " ><p><strong>Replaceable lenses</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Internal memory</strong></p></td><td  ><p>47GB</p></td><td  ><p>106GB</p></td><td  ><p>None</p></td><td  ><p>None</p></td></tr><tr><td class="firstcol " ><p><strong>MicroSD slot</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Pre-recording</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td></tr><tr><td class="firstcol " ><p><strong>Wireless mic hookup</strong></p></td><td  ><p>Yes</p></td><td  ><p>Yes</p></td><td  ><p>Yes</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><td  ><p>Bluetooth, Wi-Fi, USB-C</p></td><td  ><p>Bluetooth, Wi-Fi, USB-C</p></td><td  ><p>Bluetooth, Wi-Fi, USB-C</p></td></tr><tr><td class="firstcol " ><p><strong>Waterproofing</strong></p></td><td  ><p>66ft (20m)</p></td><td  ><p>33ft (10m)</p></td><td  ><p>16ft (5m)</p></td><td  ><p>49ft (15m)</p></td></tr><tr><td class="firstcol " ><p><strong>Battery capacity</strong></p></td><td  ><p>2,600mAh</p></td><td  ><p>1,950mAh</p></td><td  ><p>1,960mAh</p></td><td  ><p>2,400mAh</p></td></tr><tr><td class="firstcol " ><p><strong>Tested 360 runtime (8K/30fps)</strong></p></td><td  ><p>155 mins</p></td><td  ><p>112 mins</p></td><td  ><p>73 mins</p></td><td  ><p>72 mins</p></td></tr><tr><td class="firstcol " ><p><strong>Tested single-lens runtime (4K/60fps)</strong></p></td><td  ><p>117 mins</p></td><td  ><p>112 mins</p></td><td  ><p>90 mins</p></td><td  ><p>87 mins</p></td></tr><tr><td class="firstcol " ><p><strong>Size</strong></p></td><td  ><p>3.9 x 2 x 1.6 inches</p></td><td  ><p>2.4 x 1.4 x 3.1 inches</p></td><td  ><p>2.5 x 2.7 x 1.9 inches</p></td><td  ><p>4.9 x 1.8 x 1.5 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>6.9 ounces</p></td><td  ><p>6.4 ounces</p></td><td  ><p>6.8 ounces</p></td><td  ><p>7.05 ounces</p></td></tr></tbody></table></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I added this thermal camera to my mobile photography kit —  and it completely transformed my outdoor walks ]]></title>
                                                                                                <dc:content><![CDATA[ <p>A few weeks back, during one of my late-night running workouts, I heard a rustle around the sand dunes near my usual route, but I couldn’t tell what it was in the dark. When I returned the following day with the <a href="https://thermalmaster.com/products/thermal-master-dv2" target="_blank" rel="nofollow">Thermal Master DV2</a> — a pocketable night-vision camera — I discovered it was a fox protecting its nearby den.</p><p>Now, whether I’m out on a casual walk or <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">shooting astrophotography</a> at night with any of the <a href="https://www.tomsguide.com/us/best-phone-cameras,review-2272.html">best camera phones</a> I'm testing, it’s become an essential addition to my setup. Best of all, this thermal camera isn’t just suited for nighttime use because it’s just as helpful during the day for spotting wildlife.</p><p>Here’s why a pocket thermal camera might just be the most effective scouting tool you can carry in your kit.</p><div class="product"><a data-dimension112="94b54d94-933e-11f1-a8a8-0fdb8f74e9b7" data-action="Deal Block" data-label="DV2" data-dimension48="DV2" data-dimension25="$499" href="https://www.amazon.com/Thermal-Master-Monocular-Ultra-Sensitive-Sensor/dp/B0GTY1P6BT/" 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="cQX5bCHTGo5UeRnt3X757k" name="Thermal Master DV2 deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/cQX5bCHTGo5UeRnt3X757k.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Thermal Master DV2 is a powerful thermal camera that can spot wildlife in the dark or daytime, thanks to its long 8x digital zoom to reveal birds, small critters, and animals hidden up to 1,094 yards away in total darkness or dense cover. It features a 5-inch HD touchscreen, multiple infrared modes, and an integrated laser pointer — all in a lightweight pocket-friendly design that's perfect for nature photographers and outdoor enthusiasts.<br><br><strong>Price check: </strong><a href="https://thermalmaster.com/products/thermal-master-dv2" target="_blank" rel="nofollow" data-dimension112="94b54d94-933e-11f1-a8a8-0fdb8f74e9b7" data-action="Deal Block" data-label="DV2" data-dimension48="DV2" data-dimension25="$499"><strong>$499 @ Thermal Master</strong></a><a class="view-deal button" href="https://www.amazon.com/Thermal-Master-Monocular-Ultra-Sensitive-Sensor/dp/B0GTY1P6BT/" target="_blank" rel="nofollow" data-dimension112="94b54d94-933e-11f1-a8a8-0fdb8f74e9b7" data-action="Deal Block" data-label="DV2" data-dimension48="DV2" data-dimension25="$499">View Deal</a></p></div><h2 id="camcorder-style-ergonomics">Camcorder-style ergonomics</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/5rmD8UE3zPaz6GEPwDqEj6.jpg" alt="Thermal Master DV2 hands-on." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/5D6Dgz8KWTfLPzT6DYiSm6.jpg" alt="Thermal Master DV2 hands-on." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4FUabJL5hhfide8fBYGUi6.jpg" alt="Thermal Master DV2 hands-on." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>What I love most about the Thermal Master DV2 is how it’s designed like the handheld camcorders I used to carry in the early 2000s. It balances nostalgic ergonomics with modern portability, letting me slip it effortlessly into my shoulder bag or pocket.</p><p>Unlike all the other attachable thermal cameras for phones I’ve tested, like the <a href="https://www.tomsguide.com/phones/forget-flir-i-tried-this-cheap-thermal-camera-for-my-phone-to-see-in-the-dark">P1</a>, <a href="https://www.tomsguide.com/features/i-tried-this-thermal-camera-to-see-potential-dangers-around-my-home-heres-what-i-found">P2</a>, and <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</a>, the DV2 acts as a standalone unit. That means it works entirely on its own with a 5-inch adjustable display and a grip-style design — no phone connection required. There’s also a standard ¼-inch mount on the bottom that gives it added stability when fastened to my monopod.</p><p>If I have one complaint about the design, it’s that the two buttons on the grip can’t be customized for other functions. By default, the top button activates the built-in laser pointer, while the bottom one cycles through the different infrared palettes. I just wish I could remap them to actions like snapping a photo or starting a video recording.</p><h2 id="cuts-through-dense-foliage">Cuts through dense foliage</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="H7GsyKaV24LnEMLgi3oEo6" name="Thermal Master DV2 hands-on" alt="Thermal Master DV2 hands-on." src="https://cdn.mos.cms.futurecdn.net/H7GsyKaV24LnEMLgi3oEo6.jpg" mos="" align="middle" fullscreen="" width="2000" height="1124" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: John Velasco / Tom's Guide)</span></figcaption></figure><p>Smartphone thermal attachments are fine for close-range tasks like finding drafty windows or plumbing leaks, but in my experience, they fall short when trying to identify distant targets in the wild. They simply look like blobs from far away. That’s precisely why the Thermal Master DV2 earns a spot on my late-night runs.</p><p>Its high-sensitivity thermal camera cuts right through dense branches and thick underbrush. Animals hidden deep within cover immediately stand out once I dial into the right AI profile, glowing bright against the cooler foliage on the 5-inch screen.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/qjc6rE9PCNoXd2KrnjiU8f.gif" alt="Thermal Master DV2 sample." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LpgLEV7nhzckFUuiHWdxnf.gif" alt="Thermal Master DV2 sample." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6M93wR8wwrghGDijNDkYqe.gif" alt="Thermal Master DV2 sample." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/R5cbLAYfLfGjkBpyYERJbe.gif" alt="Thermal Master DV2 sample." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>Thanks to its telephoto lens and manual focus ring, I can pull in crisp footage even at full 8x digital zoom — delivering detail that easily outperforms the smartphone dongles I’ve used. By pairing manual focus precision with intelligent target algorithms, the DV2 removes all the guesswork from nighttime scouting, making it easy to locate subjects before bringing up my phone camera for the final shot.</p><h2 id="high-price-higher-value">High price, higher value</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="5rmD8UE3zPaz6GEPwDqEj6" name="Thermal Master DV2 hands-on" alt="Thermal Master DV2 held in hand." src="https://cdn.mos.cms.futurecdn.net/5rmD8UE3zPaz6GEPwDqEj6.jpg" mos="" align="middle" fullscreen="" width="2000" height="1124" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: John Velasco / Tom's Guide)</span></figcaption></figure><p>At $499, the Thermal Master DV2 is a serious investment that might give casual buyers pause, especially with so many cheap smartphone dongles on the market. But the cost is easily justified by what you get: a dedicated telephoto core, a zero-lag 50Hz refresh rate, and the convenience of a standalone hardware design.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2999px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D7bv3syzckVeaXtVPgAyFi" name="Thermal Master - duckling" alt="Thermal Master - duckling." src="https://cdn.mos.cms.futurecdn.net/D7bv3syzckVeaXtVPgAyFi.jpg" mos="" align="middle" fullscreen="" width="2999" height="1687" 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>That power makes it far more versatile than a simple wildlife scouting tool. Whether I’m diagnosing thermal leaks around the house or stepping in for unexpected neighborhood emergencies, it continually proves its value. Recently, I even used it to help a neighbor rescue ducklings trapped in a storm drain — shining the DV2 down the pitch-black tunnel to confirm every last duckling was safe and accounted for.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ONqrnW"></div>                            </div>                            <script src="https://kwizly.com/embed/ONqrnW.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/samsung-phones/i-used-a-thermal-camera-to-see-how-hot-the-galaxy-z-fold-8-ultra-gets-compared-to-the-galaxy-s26-ultra-heres-what-happened">I used a thermal camera to see how hot the Galaxy Z Fold 8 Ultra gets compared to the Galaxy S26 Ultra — here's what happened</a></li><li><a href="https://www.tomsguide.com/phones/samsung-phones/samsung-galaxy-z-fold-8-ultra-vs-galaxy-s26-ultra">I tested the Samsung Galaxy Z Fold 8 Ultra vs Galaxy S26 Ultra for 2 weeks — here’s the one I’d buy</a></li><li><a href="https://www.tomsguide.com/phones/samsung-phones/i-just-tried-the-new-samsung-dex-on-the-galaxy-z-fold-8-and-it-has-one-huge-upgrade">I just tried the new Samsung DeX on the Galaxy Z Fold 8 — and it has one huge upgrade</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/phones/i-added-this-thermal-camera-to-my-mobile-photography-kit-and-it-completely-transformed-my-outdoor-walks</link>
                                                                            <description>
                            <![CDATA[ The Thermal Master DV2 is a standalone, camcorder-style thermal camera that turns your smartphone into wildlife-scouting rig. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">MbXBJkYspGYk2TCSuqYmYW</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/RFJJtY7GqPVpNgXx96okn6-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Mon, 10 Aug 2026 06:00:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Phones]]></category>
                                                    <category><![CDATA[Cameras & 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/RFJJtY7GqPVpNgXx96okn6-1280-80.jpg">
                                                            <media:credit><![CDATA[John Velasco / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Thermal Master DV2 hands-on.]]></media:description>                                                            <media:text><![CDATA[Thermal Master DV2 hands-on.]]></media:text>
                                <media:title type="plain"><![CDATA[Thermal Master DV2 hands-on.]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/RFJJtY7GqPVpNgXx96okn6-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>A few weeks back, during one of my late-night running workouts, I heard a rustle around the sand dunes near my usual route, but I couldn’t tell what it was in the dark. When I returned the following day with the <a href="https://thermalmaster.com/products/thermal-master-dv2" target="_blank" rel="nofollow">Thermal Master DV2</a> — a pocketable night-vision camera — I discovered it was a fox protecting its nearby den.</p><p>Now, whether I’m out on a casual walk or <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">shooting astrophotography</a> at night with any of the <a href="https://www.tomsguide.com/us/best-phone-cameras,review-2272.html">best camera phones</a> I'm testing, it’s become an essential addition to my setup. Best of all, this thermal camera isn’t just suited for nighttime use because it’s just as helpful during the day for spotting wildlife.</p><p>Here’s why a pocket thermal camera might just be the most effective scouting tool you can carry in your kit.</p><div class="product"><a data-dimension112="94b54d94-933e-11f1-a8a8-0fdb8f74e9b7" data-action="Deal Block" data-label="DV2" data-dimension48="DV2" data-dimension25="$499" href="https://www.amazon.com/Thermal-Master-Monocular-Ultra-Sensitive-Sensor/dp/B0GTY1P6BT/" 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="cQX5bCHTGo5UeRnt3X757k" name="Thermal Master DV2 deal block" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/cQX5bCHTGo5UeRnt3X757k.jpg" mos="" align="middle" fullscreen="" width="600" height="600" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Thermal Master DV2 is a powerful thermal camera that can spot wildlife in the dark or daytime, thanks to its long 8x digital zoom to reveal birds, small critters, and animals hidden up to 1,094 yards away in total darkness or dense cover. It features a 5-inch HD touchscreen, multiple infrared modes, and an integrated laser pointer — all in a lightweight pocket-friendly design that's perfect for nature photographers and outdoor enthusiasts.<br><br><strong>Price check: </strong><a href="https://thermalmaster.com/products/thermal-master-dv2" target="_blank" rel="nofollow" data-dimension112="94b54d94-933e-11f1-a8a8-0fdb8f74e9b7" data-action="Deal Block" data-label="DV2" data-dimension48="DV2" data-dimension25="$499"><strong>$499 @ Thermal Master</strong></a><a class="view-deal button" href="https://www.amazon.com/Thermal-Master-Monocular-Ultra-Sensitive-Sensor/dp/B0GTY1P6BT/" target="_blank" rel="nofollow" data-dimension112="94b54d94-933e-11f1-a8a8-0fdb8f74e9b7" data-action="Deal Block" data-label="DV2" data-dimension48="DV2" data-dimension25="$499">View Deal</a></p></div><h2 id="camcorder-style-ergonomics">Camcorder-style ergonomics</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/5rmD8UE3zPaz6GEPwDqEj6.jpg" alt="Thermal Master DV2 hands-on." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/5D6Dgz8KWTfLPzT6DYiSm6.jpg" alt="Thermal Master DV2 hands-on." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/4FUabJL5hhfide8fBYGUi6.jpg" alt="Thermal Master DV2 hands-on." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>What I love most about the Thermal Master DV2 is how it’s designed like the handheld camcorders I used to carry in the early 2000s. It balances nostalgic ergonomics with modern portability, letting me slip it effortlessly into my shoulder bag or pocket.</p><p>Unlike all the other attachable thermal cameras for phones I’ve tested, like the <a href="https://www.tomsguide.com/phones/forget-flir-i-tried-this-cheap-thermal-camera-for-my-phone-to-see-in-the-dark">P1</a>, <a href="https://www.tomsguide.com/features/i-tried-this-thermal-camera-to-see-potential-dangers-around-my-home-heres-what-i-found">P2</a>, and <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</a>, the DV2 acts as a standalone unit. That means it works entirely on its own with a 5-inch adjustable display and a grip-style design — no phone connection required. There’s also a standard ¼-inch mount on the bottom that gives it added stability when fastened to my monopod.</p><p>If I have one complaint about the design, it’s that the two buttons on the grip can’t be customized for other functions. By default, the top button activates the built-in laser pointer, while the bottom one cycles through the different infrared palettes. I just wish I could remap them to actions like snapping a photo or starting a video recording.</p><h2 id="cuts-through-dense-foliage">Cuts through dense foliage</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="H7GsyKaV24LnEMLgi3oEo6" name="Thermal Master DV2 hands-on" alt="Thermal Master DV2 hands-on." src="https://cdn.mos.cms.futurecdn.net/H7GsyKaV24LnEMLgi3oEo6.jpg" mos="" align="middle" fullscreen="" width="2000" height="1124" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: John Velasco / Tom's Guide)</span></figcaption></figure><p>Smartphone thermal attachments are fine for close-range tasks like finding drafty windows or plumbing leaks, but in my experience, they fall short when trying to identify distant targets in the wild. They simply look like blobs from far away. That’s precisely why the Thermal Master DV2 earns a spot on my late-night runs.</p><p>Its high-sensitivity thermal camera cuts right through dense branches and thick underbrush. Animals hidden deep within cover immediately stand out once I dial into the right AI profile, glowing bright against the cooler foliage on the 5-inch screen.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/qjc6rE9PCNoXd2KrnjiU8f.gif" alt="Thermal Master DV2 sample." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LpgLEV7nhzckFUuiHWdxnf.gif" alt="Thermal Master DV2 sample." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6M93wR8wwrghGDijNDkYqe.gif" alt="Thermal Master DV2 sample." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/R5cbLAYfLfGjkBpyYERJbe.gif" alt="Thermal Master DV2 sample." /><figcaption><small role="credit">John Velasco / Tom's Guide</small></figcaption></figure></figure><p>Thanks to its telephoto lens and manual focus ring, I can pull in crisp footage even at full 8x digital zoom — delivering detail that easily outperforms the smartphone dongles I’ve used. By pairing manual focus precision with intelligent target algorithms, the DV2 removes all the guesswork from nighttime scouting, making it easy to locate subjects before bringing up my phone camera for the final shot.</p><h2 id="high-price-higher-value">High price, higher value</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2000px;"><p class="vanilla-image-block" style="padding-top:56.20%;"><img id="5rmD8UE3zPaz6GEPwDqEj6" name="Thermal Master DV2 hands-on" alt="Thermal Master DV2 held in hand." src="https://cdn.mos.cms.futurecdn.net/5rmD8UE3zPaz6GEPwDqEj6.jpg" mos="" align="middle" fullscreen="" width="2000" height="1124" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: John Velasco / Tom's Guide)</span></figcaption></figure><p>At $499, the Thermal Master DV2 is a serious investment that might give casual buyers pause, especially with so many cheap smartphone dongles on the market. But the cost is easily justified by what you get: a dedicated telephoto core, a zero-lag 50Hz refresh rate, and the convenience of a standalone hardware design.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2999px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="D7bv3syzckVeaXtVPgAyFi" name="Thermal Master - duckling" alt="Thermal Master - duckling." src="https://cdn.mos.cms.futurecdn.net/D7bv3syzckVeaXtVPgAyFi.jpg" mos="" align="middle" fullscreen="" width="2999" height="1687" 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>That power makes it far more versatile than a simple wildlife scouting tool. Whether I’m diagnosing thermal leaks around the house or stepping in for unexpected neighborhood emergencies, it continually proves its value. Recently, I even used it to help a neighbor rescue ducklings trapped in a storm drain — shining the DV2 down the pitch-black tunnel to confirm every last duckling was safe and accounted for.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-ONqrnW"></div>                            </div>                            <script src="https://kwizly.com/embed/ONqrnW.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/samsung-phones/i-used-a-thermal-camera-to-see-how-hot-the-galaxy-z-fold-8-ultra-gets-compared-to-the-galaxy-s26-ultra-heres-what-happened">I used a thermal camera to see how hot the Galaxy Z Fold 8 Ultra gets compared to the Galaxy S26 Ultra — here's what happened</a></li><li><a href="https://www.tomsguide.com/phones/samsung-phones/samsung-galaxy-z-fold-8-ultra-vs-galaxy-s26-ultra">I tested the Samsung Galaxy Z Fold 8 Ultra vs Galaxy S26 Ultra for 2 weeks — here’s the one I’d buy</a></li><li><a href="https://www.tomsguide.com/phones/samsung-phones/i-just-tried-the-new-samsung-dex-on-the-galaxy-z-fold-8-and-it-has-one-huge-upgrade">I just tried the new Samsung DeX on the Galaxy Z Fold 8 — and it has one huge upgrade</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I took my Insta360 Go 3S Retro on-the-move with me for a month — here's how it's changed the way I take photos ]]></title>
                                                                                                <dc:content><![CDATA[ <p>I love fun photos taken on an equally fun camera. And as a novice photographer, I've always been curious about Insta360's cameras.</p><p>Earlier this year, Insta360 introduced a special edition of its thumb-sized Go 3S camera that blends modern 4K tech with nostalgic style. Dubbed the <a href="https://www.amazon.com.au/Insta360-Retro-Bundle-Special-128GB/dp/B0GSR25P9R" target="_blank">Insta360 Go 3S Retro</a> Bundle, this new bundle arrives two years after the original Go 3S. 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 with outstanding features — namely 4K video recording, superb battery life, and cool new functions accessible via its smartphone app.</p><p>The main difference between the Go 3S and the Go 3S Retro lies in the included accessories. The most obvious is the Retro viewfinder, which pairs with the standalone camera to create a handheld shooting experience. </p><p>As a fan of vintage-inspired cameras, the Go 3S Retro immediately caught my eye. Now, I'm no camera expert — I consider myself a total newbie in most cases — but I was beyond keen to get my hands on this tiny cam, for several reasons. Aside from primarily wanting to take some fun panoramic shots, I also wanted to capture some <del>important</del> POVs — a.k.a. what my dogs see — and take the cam for a spin when creating content. And it didn't disappoint. </p><p>Over the course of a month, I took my Go 3S Retro on every dog walk, each mosey around the shops, the occasional bike ride or drive, and everything in between. During each occasion, I noticed two major factors that made me gravitate towards using the Retro Go 3S even more — its portability and, of course, its perspective. </p><h2 id="perfect-to-pack-and-go">Perfect to pack and go</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Yu2ghXCyqzrxp4enMuUiyT.jpg" alt="Insta360 lifestyle images featuring a dog" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pZerbkFpKVw66zTdj7bvJX.jpg" alt="Insta360 lifestyle images featuring a dog" /><figcaption>Fletcher is ready to shoot some videos.<small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LqyE5X463AFz2c8BTSBGhJ.jpg" alt="Hand holding Insat360 camera while wearing the waist viewfinder" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>Now, the biggest, most significant factor for me when taking photos for work or play is accessibility. I want to use the camera easily, and I want it to work now. I typically resort to using my phone in almost every instance, as I can easily rely on it to take a decent photo without much fuss. </p><p>While the Insta360 Go 3S captures visually appealing 9MP photos in 16:9, they aren't necessarily superior to standard smartphone shots. That's because the tiny cam was made for shooting 4K videos. </p><p>That said, what I really appreciated during my test period with the Go 3S Retro was its portability. When paired with the viewfinder lanyard, it was easy enough to carry crossbody while walking my pups in our local park, and when filming content at an event, I could whip it out of my bag and clip it to my shirt with the magnetic strap for a direct POV shot. At just 39g, it felt light as a feather compared to my iPhone.</p><iframe src="https://content.jwplatform.com/players/d30w1tSz.html" id="d30w1tSz" title="Temp Video 1786313123149 (1)" width="1920" height="1084" frameborder="0" scrolling="auto" allowfullscreen></iframe><p>Better yet, I found myself editing my images and videos less. This was perhaps due to the respectable resolution, or simply because the Go 3S Retro captured the perfect shot. I didn't have to fuss over framing, angles, or distance — the Go 3S Retro handled it all.</p><p>It captured more angles and depth than I could with my phone, making it easier to edit (again, only if needed), and I found myself taking fewer photos and videos because of it. The Insta360 app also features quick transfers and previews, along with solid editing tools that eliminate the guesswork of cleaning up my shots. Overall, I definitely leaned more towards the Go 3S Retro when capturing content, and I don't think I'm going to stop anytime soon.</p><h2 id="changing-my-perspective">Changing my perspective</h2><p>Sometimes a new perspective is all you need — and the Go 3S Retro has an ultrawide view of everything. Despite being small in stature, as I've mentioned, it can capture a lot — especially things that are overlooked or missed completely. </p><p>I really enjoyed reviewing my footage after the fact, and admiring what I didn't necessarily intend to capture. From peaceful moments with family and friends to gorgeous views of Sydney, there were so many memories I got to capture — and I really loved it. </p><p>Better still, there were several modes I enjoyed using and a few that are still on my to-do list (namely a starlapse). My most-used settings so far are SlowMo and Timelapse, which have been particularly fun when unboxing review units and watching my dog's hair cinematically blow in the wind. </p><iframe src="https://content.jwplatform.com/players/W0ZvBPAT.html" id="W0ZvBPAT" title="39C58740-669B-480C-A10E-BD756B05A9AB" width="600" height="338" frameborder="0" scrolling="auto" allowfullscreen></iframe><p>Overall, using the tiny cam also allowed me to be fully present in some regards too. I could easily connect it to a magnetic surface and sit back, all without using my phone (or hands in some cases) to capture the moment. </p><p>So, after spending a month with the Insta360 Go 3S Retro, it became crystal clear to me that this thumb-sized camera offers a refreshing shift in how we capture memories. By prioritising portability and unique, hands-free perspectives over the fuss of traditional smartphone photography, the camera allows you to stay present while effortlessly recording life's moments. </p><p>With features like convenient magnetic mounting, high-quality 4K video, and intuitive app-based editing, the Go 3S Retro simplifies content creation, proving that a smaller camera can indeed capture a much bigger picture.</p><div data-widget-type="simple" data-model-name="Insta360 GO 3S Retro Bundle"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/i-took-my-insta360-go-3s-retro-on-the-move-with-me-for-a-month-heres-how-its-changed-the-way-i-take-photos</link>
                                                                            <description>
                            <![CDATA[ I tried Insta360's newest thumb-sized camera, the Go 3S Retro, for a month. Here's how I fared. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">B8aXHFyKZXZev8esAswN2L</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/QCfkePpvGYPVvgePaBW6KV-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Sun, 09 Aug 2026 23:01:36 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras & 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/QCfkePpvGYPVvgePaBW6KV-1280-80.jpg">
                                                            <media:credit><![CDATA[Future / Lucy Scotting]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[Insta360 lifestyle images featuring a dog]]></media:description>                                                            <media:text><![CDATA[Insta360 lifestyle images featuring a dog]]></media:text>
                                <media:title type="plain"><![CDATA[Insta360 lifestyle images featuring a dog]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/QCfkePpvGYPVvgePaBW6KV-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>I love fun photos taken on an equally fun camera. And as a novice photographer, I've always been curious about Insta360's cameras.</p><p>Earlier this year, Insta360 introduced a special edition of its thumb-sized Go 3S camera that blends modern 4K tech with nostalgic style. Dubbed the <a href="https://www.amazon.com.au/Insta360-Retro-Bundle-Special-128GB/dp/B0GSR25P9R" target="_blank">Insta360 Go 3S Retro</a> Bundle, this new bundle arrives two years after the original Go 3S. 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 with outstanding features — namely 4K video recording, superb battery life, and cool new functions accessible via its smartphone app.</p><p>The main difference between the Go 3S and the Go 3S Retro lies in the included accessories. The most obvious is the Retro viewfinder, which pairs with the standalone camera to create a handheld shooting experience. </p><p>As a fan of vintage-inspired cameras, the Go 3S Retro immediately caught my eye. Now, I'm no camera expert — I consider myself a total newbie in most cases — but I was beyond keen to get my hands on this tiny cam, for several reasons. Aside from primarily wanting to take some fun panoramic shots, I also wanted to capture some <del>important</del> POVs — a.k.a. what my dogs see — and take the cam for a spin when creating content. And it didn't disappoint. </p><p>Over the course of a month, I took my Go 3S Retro on every dog walk, each mosey around the shops, the occasional bike ride or drive, and everything in between. During each occasion, I noticed two major factors that made me gravitate towards using the Retro Go 3S even more — its portability and, of course, its perspective. </p><h2 id="perfect-to-pack-and-go">Perfect to pack and go</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/Yu2ghXCyqzrxp4enMuUiyT.jpg" alt="Insta360 lifestyle images featuring a dog" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/pZerbkFpKVw66zTdj7bvJX.jpg" alt="Insta360 lifestyle images featuring a dog" /><figcaption>Fletcher is ready to shoot some videos.<small role="credit">Future / Lucy Scotting</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/LqyE5X463AFz2c8BTSBGhJ.jpg" alt="Hand holding Insat360 camera while wearing the waist viewfinder" /><figcaption><small role="credit">Future / Lucy Scotting</small></figcaption></figure></figure><p>Now, the biggest, most significant factor for me when taking photos for work or play is accessibility. I want to use the camera easily, and I want it to work now. I typically resort to using my phone in almost every instance, as I can easily rely on it to take a decent photo without much fuss. </p><p>While the Insta360 Go 3S captures visually appealing 9MP photos in 16:9, they aren't necessarily superior to standard smartphone shots. That's because the tiny cam was made for shooting 4K videos. </p><p>That said, what I really appreciated during my test period with the Go 3S Retro was its portability. When paired with the viewfinder lanyard, it was easy enough to carry crossbody while walking my pups in our local park, and when filming content at an event, I could whip it out of my bag and clip it to my shirt with the magnetic strap for a direct POV shot. At just 39g, it felt light as a feather compared to my iPhone.</p><iframe src="https://content.jwplatform.com/players/d30w1tSz.html" id="d30w1tSz" title="Temp Video 1786313123149 (1)" width="1920" height="1084" frameborder="0" scrolling="auto" allowfullscreen></iframe><p>Better yet, I found myself editing my images and videos less. This was perhaps due to the respectable resolution, or simply because the Go 3S Retro captured the perfect shot. I didn't have to fuss over framing, angles, or distance — the Go 3S Retro handled it all.</p><p>It captured more angles and depth than I could with my phone, making it easier to edit (again, only if needed), and I found myself taking fewer photos and videos because of it. The Insta360 app also features quick transfers and previews, along with solid editing tools that eliminate the guesswork of cleaning up my shots. Overall, I definitely leaned more towards the Go 3S Retro when capturing content, and I don't think I'm going to stop anytime soon.</p><h2 id="changing-my-perspective">Changing my perspective</h2><p>Sometimes a new perspective is all you need — and the Go 3S Retro has an ultrawide view of everything. Despite being small in stature, as I've mentioned, it can capture a lot — especially things that are overlooked or missed completely. </p><p>I really enjoyed reviewing my footage after the fact, and admiring what I didn't necessarily intend to capture. From peaceful moments with family and friends to gorgeous views of Sydney, there were so many memories I got to capture — and I really loved it. </p><p>Better still, there were several modes I enjoyed using and a few that are still on my to-do list (namely a starlapse). My most-used settings so far are SlowMo and Timelapse, which have been particularly fun when unboxing review units and watching my dog's hair cinematically blow in the wind. </p><iframe src="https://content.jwplatform.com/players/W0ZvBPAT.html" id="W0ZvBPAT" title="39C58740-669B-480C-A10E-BD756B05A9AB" width="600" height="338" frameborder="0" scrolling="auto" allowfullscreen></iframe><p>Overall, using the tiny cam also allowed me to be fully present in some regards too. I could easily connect it to a magnetic surface and sit back, all without using my phone (or hands in some cases) to capture the moment. </p><p>So, after spending a month with the Insta360 Go 3S Retro, it became crystal clear to me that this thumb-sized camera offers a refreshing shift in how we capture memories. By prioritising portability and unique, hands-free perspectives over the fuss of traditional smartphone photography, the camera allows you to stay present while effortlessly recording life's moments. </p><p>With features like convenient magnetic mounting, high-quality 4K video, and intuitive app-based editing, the Go 3S Retro simplifies content creation, proving that a smaller camera can indeed capture a much bigger picture.</p><div data-widget-type="simple" data-model-name="Insta360 GO 3S Retro Bundle"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve spent a month testing this Lomography reloadable film camera — and for $27, it offers unbeatable value for money ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Film cameras have made a comeback in earnest, as my social media feed is heavily populated with retro-style photos. You don’t need to spend hundreds of dollars on a good film camera either — take the Lomography Simple Use, for instance. It’s one of the <a href="https://www.tomsguide.com/us/best-cheap-cameras,review-6319.html">best cheap cameras</a> around, as for just $27, it comes pre-loaded with a 27-exposure roll of film and an AA battery to power its built-in flash.</p><p>Alongside the flash, the Simple Use also packs a set of colored gel filters to help you get creative shots. It’s extremely easy to use (as you’ve probably guessed from the name), and the photos it takes have a retro, vintage look to them. However, film removal is slightly fiddly; build quality isn’t the best; and there’s some chromatic aberration in the photos.</p><p>Should you still buy it? Read my full Lomography Simple Use Reloadable Film Camera Classicolor review.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-specs"><span>Lomography Simple Use Reloadable Film Camera Classicolor review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://shop.lomography.com/uk/simple-use-reloadable-film-camera-classicolor" target="_blank" rel="nofollow">$27 / £19</a></p></td></tr><tr><td class="firstcol " ><p><strong>Lens</strong></p></td><td  ><p>31mm fixed-focus</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>Exposure control</strong></p></td><td  ><p>Automatic</p></td></tr><tr><td class="firstcol " ><p><strong>Shutter</strong></p></td><td  ><p>1/120s</p></td></tr><tr><td class="firstcol " ><p><strong>Aperture</strong></p></td><td  ><p>f/9</p></td></tr><tr><td class="firstcol " ><p><strong>ISO</strong></p></td><td  ><p>200 / 400</p></td></tr><tr><td class="firstcol " ><p><strong>Shooting range</strong></p></td><td  ><p>1m to ∞</p></td></tr><tr><td class="firstcol " ><p><strong>Flash</strong></p></td><td  ><p>Built-in</p></td></tr><tr><td class="firstcol " ><p><strong>Self-timer</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>1x AA</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>4.5 x 2.4 x 1.3 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>4.23oz (including film and battery)</p></td></tr><tr><td class="firstcol " ><p><strong>Film format</strong></p></td><td  ><p>35mm full frame</p></td></tr><tr><td class="firstcol " ><p><strong>Film development cost</strong></p></td><td  ><p>$20 / £18 approx.</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-price-availability"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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="6godXDrFmrymYCQ7hrZa6h" name="Lomography_.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/6godXDrFmrymYCQ7hrZa6h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lomography Simple Use Reloadable Film Camera Classicolor (I’ll just use ‘Simple Use’ from now on) is a very cheap and very cheerful film camera. If you buy it directly from Lomography, the Simple Use costs <a href="https://shop.lomography.com/uk/simple-use-reloadable-film-camera-classicolor" target="_blank" rel="nofollow">$27 / £19</a>. In the U.K. where I’m based, you can also buy the Simple Use from Amazon, but this drives the cost up to <a href="https://www.amazon.co.uk/Lomography-Simple-Camera-Negative-SUC100CN/dp/B07QP4RM6S/" target="_blank" rel="nofollow">£52</a>.</p><p>You can get either an ISO 400 or an ISO 200 (tested) model, and both come pre-loaded with a 27-exposure roll of film, so you get your first shots for free. ISO 400 film is twice as sensitive to light as ISO 200 film, allowing faster shutter speeds and better low-light performance. If you want to photograph moving subjects, shade and cloudy days better, ISO 400 is right for you. If you want a camera that works best in bright, sunny outdoor conditions, ISO 200 should suffice.</p><p>The Simple Use is much cheaper than other reusable film cameras, like the <a href="https://www.tomsguide.com/cameras-photography/kodak-ultra-f9-35mm-review">Kodak Ultra F9 35mm</a> ($49), the <a href="https://www.tomsguide.com/cameras-photography/kodak-ektar-h35-half-frame-review">Kodak Ektar H35</a> ($49) and the AgfaPhoto Analogue 35mm (<a href="https://www.amazon.com/AgfaPhoto-Analogue-Photo-Camera-Battery/dp/B0BQ7JTPWK/" target="_blank" rel="nofollow">$81</a>). It’s priced similarly to the Fujifilm QuickSnap Flash 400 (<a href="https://www.amazon.com/Fujifilm-QuickSnap-Flash-One-Time-Use-Camera/dp/B0F25C5257/" target="_blank" rel="nofollow">$21</a>) and the Kodak FunSaver 35mm (<a href="https://www.amazon.com/Kodak-FunSaver-35mm-Single-Camera/dp/B00001R3W3/" target="_blank" rel="nofollow">$29</a>) but they’re single-use cameras, so you can only use them for one roll of film. The Simple Use is, then, excellent value for money.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-design-build-quality"><span>Lomography Simple Use Reloadable Film Camera Classicolor review: Design & build 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="r58cqNTa9uA3fc64ox3CGb" name="Lomo_Simple_Use-2" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/r58cqNTa9uA3fc64ox3CGb.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 want a compact film camera that doesn’t take up much space in your handbag, the Lomography Simple Use is the one. The camera measures just 4.5 x 2.4 x 1.3 inches and weighs 4.23oz (including film and battery), so it’s similar to other film cameras, like the Kodak Ultra F9 and the Kodak Ektar H35. I’ve had no trouble carrying the Simple Use in my trousers’ pocket.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xfu3eN4xWuVVk2Pm6QQu3h" name="Lomography_ 1.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/xfu3eN4xWuVVk2Pm6QQu3h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Simple Use comes in a few different colors, such as yellow, red, purple, orange, and turquoise. Each model is wrapped in stickers around the front and the back, with usage instructions — handy if this is your first time using a film camera. I tested the yellow model which looks fine. The aesthetics aren’t anything to write home about, and neither is the build quality.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5172px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="zsk9rxBd4hkYx2Fqi7vu3c" name="Lomo-Simple-Use-2" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/zsk9rxBd4hkYx2Fqi7vu3c.jpg" mos="" align="middle" fullscreen="" width="5172" height="2909" 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>Annoyingly, the Simple Use is made entirely of ABS plastic which feels cheap and flimsy. I feel like if I exerted even a bit of pressure on the film door’s hinges, it would fall apart. But you have to remind yourself that this camera costs just under $30. If build quality is a priority for you, I’d recommend the Kodak Ultra F9 instead — but you’ll need to spend an extra $20 for it.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-lens-viewfinder"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="FYFf3bLPShu2NjDbWYH46h" name="Lomography_ 7.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/FYFf3bLPShu2NjDbWYH46h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lomography Simple Use is fitted with a 31mm fixed-focus lens. This provides a wild field of view which means you can capture a lot of the scene, making the Simple Use great for street, landscapes, travel and everyday photography. The camera has a focusing range of 1m to ∞, so any subject closer than 1 meter will be blurry and out of 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fHHVhw9Yh6Xi4j9mBpRC7h" name="Lomography_ 5.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/fHHVhw9Yh6Xi4j9mBpRC7h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As a no-frills film camera, you don’t get any manual control over focus settings, so the Simple Use is excellent for beginners who just want to capture memories without thinking too much.</p><p>The Simple Use features a small viewfinder that I found easy to look through, even as someone who wears prescription glasses.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-controls"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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:6862px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="omPxyPXMdssawQtDfc3UHg" name="Lomo-Simple-Use" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/omPxyPXMdssawQtDfc3UHg.jpg" mos="" align="middle" fullscreen="" width="6862" height="3860" 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>Lomography hit the nail on the head when it called this camera Simple Use, because it is extremely simple to use. It comes pre-loaded with film, so is ready to use as soon as you unbox it. </p><p>All the shooting settings are adjusted automatically so all you need to do is look through the viewfinder, frame your subject, and press the shutter button to take a photo. I bet my family’s Golden Retriever, Odie, could use the Simple Use too… if he had opposable thumbs.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="He6FCTpFLbg5rLTXugAh9h" name="Lomography_ 4.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/He6FCTpFLbg5rLTXugAh9h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The film advance dial is located right below the shutter button, so it’s easy to advance the film to the next frame. Unloading the film is extremely straightforward, too. You’ll know the roll has finished when the film advance dial keeps spinning. After that, press the film door button to open the door, and take your roll out. Frustratingly, though, the film door button is quite stiff and takes a couple of tries before you can push it to open the door.</p><p>Since this is a reusable camera, you can pop in a new roll of film, close the door, and you’re ready to start shooting again.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="deJxcRjindAxauh8uXx97h" name="Lomography_ 6.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/deJxcRjindAxauh8uXx97h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Simple Use features a built-in flash too. To use it, you need to hold down the flash button located on the front of the camera, and take a photo while keeping it pressed.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="i8QE7mpJ6DTnQrpyfr2K6h" name="Lomography_ 2.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/i8QE7mpJ6DTnQrpyfr2K6h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 set of yellow, cyan and magenta colored gel filters attached next to the viewfinder, so you can move one (or all of them!) over the flash to create different color effects.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-image-quality"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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="yRn7jq8UYNsunAX5RRPj9b" name="Lomo_Simple_Use" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/yRn7jq8UYNsunAX5RRPj9b.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 Lomography Simple Use captures quintessentially retro-style images which are lo-fi and grainy — something that’s right up Gen Z’s street as retro tech has made a huge comeback these last few years. The images aren’t the most hi-res and are fit for sharing on social media. Not being able to see the photos until you get your film developed lets you stay in the moment and enjoy yourself, too.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/VN2gxMBpuToZrb3oYvkwCT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eq6nphYqQW9FvLvUfxaYDT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hCJ6Y7yh8yY56Zb89DANMT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fpa6VkWp6sp5NCon3siHQV.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6Pkdv3QagHY2vpeQHgNwLT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/SE7jDL8tb39AbsZcfRmUpW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/P7kDJ5k6ML27bCds9zu3uW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zAx7oCmyTCyFjiRaLr7MCT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>And I’ve thoroughly enjoyed using the Simple Use to photograph Bristol Pride, the city of Bath in the U.K., and my travels. Results can be hit-or-miss but that’s the charm of film cameras. I’ve been happy with most of the photos I’ve taken with the Simple Use, and you can see a few examples above. I love how fluffy and well-defined the clouds look in the first and second shots, and the plane’s red jet engine really pops.</p><p>Most of the images are quite sharp, too. In the third, fourth and fifth shots, the edges of the plaque and the buildings are well-defined. In terms of colors, the blues in the Union Jack flags and the West Country Ales plaque appear deep, while the buildings’ beige stone looks as textured as in real life.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/wCa4LQDZzkeVETpfVGAbDT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/WBvxgkKVNtBTJ6AViQHrLT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/y4QpQF9KjhdmRFmumTYMHT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/BcSprSSZmuc7ciBYqmUmET.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/c3nyHLUUuXKaH2CBXa33DT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>In the first two shots, taken on a sunny day, the colors look great. Each individual shade in the ‘Love Wins’ fan is discernible, as are the colors in the rainbow face paint. The third photo was taken on an overcast day but the image still looks pleasing to the eye.</p><p>There is some yellow and purple color fringing, also known as chromatic aberration, around the edges of the tent in the first shot, the trees in the fourth image and the chimneys and roof in the last photo. This is usually a sign of poor optics, but in a camera this cheap it’s not surprising, nor a huge issue. This also occurred with similarly-priced cameras like the Kodak F9.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/K3BigB3W4w5jrfqAje6uSS.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VKycHVe5RLGW5QUHSvHZrW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zc6EDEMeaVPAw4gW6xNduW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption>Red color filter.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/svLjvxrpWKWSFUFNkWKzqW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption>Magenta + cyan color filters.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The Simple Use’s shutter speed is limited to 1/120s and it’s got a fixed f/9 aperture, just like on the Kodak Ultra F9. The fixed f/9 aperture lets less light in than a wide aperture lens, like f/2.8 on the much pricier <a href="https://www.tomsguide.com/cameras-photography/lomography-lomo-mc-a-35-mm-film-camera-review">Lomography Lomo MC-A 35mm</a> ($549). This means that images shot in dimly lit environments turn out darker and potentially noisier, as you can see in the second shot above.</p><p>But you can salvage some of the shots with the built-in flash. The Simple Use’s built-in flash is fairly powerful, too, and it worked well to illuminate all the shots in the gallery above. The last two photos were taken with the color filters covering the flash, and they do what they’re designed to.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-film-cost-yield"><span>Lomography Simple Use Reloadable Film Camera Classicolor review: Film cost & yield</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PVWfRZUR7mEk8ETVdZSd5h" name="Lomography_ 3.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/PVWfRZUR7mEk8ETVdZSd5h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lomography Simple Use comes pre-loaded with a 27-exposure roll of film, and subsequent rolls of 35mm film aren’t very expensive either. Film costs naturally vary depending on the type you get and where you live. For example, I live in the U.K. and used a 36-exposure roll of Fujifilm 200, which generally costs <a href="https://www.amazon.com/Fujifilm-Fujicolor-Color-Negative-Film/dp/B0DHFFTTTZ/" target="_blank" rel="nofollow">$31</a> / <a href="https://www.amazon.co.uk/Fujifilm-Fujicolor-Negative-Exposures-3-Pack/dp/B00JDOZB6C/" target="_blank" rel="nofollow">£39</a> for a pack of three.</p><p>Film development costs will vary too. My film development cost me £18 for one roll of film (digital scans only). But I asked an American colleague about film developing costs in the U.S., and he said that it can cost as little as $10.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-battery-life"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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="SDjxJuCKtqFfQvMT9fqT5h" name="Lomography_ 8.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/SDjxJuCKtqFfQvMT9fqT5h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Similar to most other film cameras I’ve tested, like the Kodak Ultra F9, the Lomography Simple Use utilizes a single AA battery (included in the box). These are easily sourced and you probably have a couple spares lying around your house, too.</p><p>Lomography doesn’t specify how long an AA battery lasts as it’s primarily there to power the flash, but I was able to get through two rolls of film comfortably while occasionally using the flash.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-verdict"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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:838px;"><p class="vanilla-image-block" style="padding-top:56.32%;"><img id="6godXDrFmrymYCQ7hrZa6h" name="Lomography_.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/v2/t:193,l:194,cw:838,ch:472,q:80/6godXDrFmrymYCQ7hrZa6h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want a cheap and very cheerful film camera, you can’t go wrong with the Lomography Simple Use. As the name suggests, this is an extremely easy camera to use. It’s perfect for beginners, casual users, and anyone who wants to capture retro-style photos — albeit with slight chromatic aberration (which doesn’t necessarily ruin the images).</p><p>The Simple Use is great value for money as it costs just $27 and comes pre-loaded with a 27-exposure film roll as well as an AA battery to power the flash. It’s fitted with a set of colored filters too to help you get creative shots, too.</p><p>Though build quality isn’t anything to write home about and removing film can be slightly frustrating, those are minor issues at this price point. At the end of the day, the Simple Use is fun and it makes capturing memories easy while allowing you to stay in the moment.</p> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/lomography-simple-use-reloadable-film-camera-classicolor-review</link>
                                                                            <description>
                            <![CDATA[ The Lomography Simple Use Reloadable Film Camera takes retro photos and is easy to use. At just $27, it offers great value for money, but it has some flaws. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">SMDUCPFQk6aiarSbfBnvC7</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/WoAV8Nej7aKKqQjbvscxFb-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 07 Aug 2026 08:00:43 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/WoAV8Nej7aKKqQjbvscxFb-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera]]></media:description>                                                            <media:text><![CDATA[A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera]]></media:text>
                                <media:title type="plain"><![CDATA[A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/WoAV8Nej7aKKqQjbvscxFb-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Film cameras have made a comeback in earnest, as my social media feed is heavily populated with retro-style photos. You don’t need to spend hundreds of dollars on a good film camera either — take the Lomography Simple Use, for instance. It’s one of the <a href="https://www.tomsguide.com/us/best-cheap-cameras,review-6319.html">best cheap cameras</a> around, as for just $27, it comes pre-loaded with a 27-exposure roll of film and an AA battery to power its built-in flash.</p><p>Alongside the flash, the Simple Use also packs a set of colored gel filters to help you get creative shots. It’s extremely easy to use (as you’ve probably guessed from the name), and the photos it takes have a retro, vintage look to them. However, film removal is slightly fiddly; build quality isn’t the best; and there’s some chromatic aberration in the photos.</p><p>Should you still buy it? Read my full Lomography Simple Use Reloadable Film Camera Classicolor review.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-specs"><span>Lomography Simple Use Reloadable Film Camera Classicolor review: Specs</span></h2><div ><table><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://shop.lomography.com/uk/simple-use-reloadable-film-camera-classicolor" target="_blank" rel="nofollow">$27 / £19</a></p></td></tr><tr><td class="firstcol " ><p><strong>Lens</strong></p></td><td  ><p>31mm fixed-focus</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>Exposure control</strong></p></td><td  ><p>Automatic</p></td></tr><tr><td class="firstcol " ><p><strong>Shutter</strong></p></td><td  ><p>1/120s</p></td></tr><tr><td class="firstcol " ><p><strong>Aperture</strong></p></td><td  ><p>f/9</p></td></tr><tr><td class="firstcol " ><p><strong>ISO</strong></p></td><td  ><p>200 / 400</p></td></tr><tr><td class="firstcol " ><p><strong>Shooting range</strong></p></td><td  ><p>1m to ∞</p></td></tr><tr><td class="firstcol " ><p><strong>Flash</strong></p></td><td  ><p>Built-in</p></td></tr><tr><td class="firstcol " ><p><strong>Self-timer</strong></p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Power</strong></p></td><td  ><p>1x AA</p></td></tr><tr><td class="firstcol " ><p><strong>Dimensions</strong></p></td><td  ><p>4.5 x 2.4 x 1.3 inches</p></td></tr><tr><td class="firstcol " ><p><strong>Weight</strong></p></td><td  ><p>4.23oz (including film and battery)</p></td></tr><tr><td class="firstcol " ><p><strong>Film format</strong></p></td><td  ><p>35mm full frame</p></td></tr><tr><td class="firstcol " ><p><strong>Film development cost</strong></p></td><td  ><p>$20 / £18 approx.</p></td></tr></tbody></table></div><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-price-availability"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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="6godXDrFmrymYCQ7hrZa6h" name="Lomography_.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/6godXDrFmrymYCQ7hrZa6h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lomography Simple Use Reloadable Film Camera Classicolor (I’ll just use ‘Simple Use’ from now on) is a very cheap and very cheerful film camera. If you buy it directly from Lomography, the Simple Use costs <a href="https://shop.lomography.com/uk/simple-use-reloadable-film-camera-classicolor" target="_blank" rel="nofollow">$27 / £19</a>. In the U.K. where I’m based, you can also buy the Simple Use from Amazon, but this drives the cost up to <a href="https://www.amazon.co.uk/Lomography-Simple-Camera-Negative-SUC100CN/dp/B07QP4RM6S/" target="_blank" rel="nofollow">£52</a>.</p><p>You can get either an ISO 400 or an ISO 200 (tested) model, and both come pre-loaded with a 27-exposure roll of film, so you get your first shots for free. ISO 400 film is twice as sensitive to light as ISO 200 film, allowing faster shutter speeds and better low-light performance. If you want to photograph moving subjects, shade and cloudy days better, ISO 400 is right for you. If you want a camera that works best in bright, sunny outdoor conditions, ISO 200 should suffice.</p><p>The Simple Use is much cheaper than other reusable film cameras, like the <a href="https://www.tomsguide.com/cameras-photography/kodak-ultra-f9-35mm-review">Kodak Ultra F9 35mm</a> ($49), the <a href="https://www.tomsguide.com/cameras-photography/kodak-ektar-h35-half-frame-review">Kodak Ektar H35</a> ($49) and the AgfaPhoto Analogue 35mm (<a href="https://www.amazon.com/AgfaPhoto-Analogue-Photo-Camera-Battery/dp/B0BQ7JTPWK/" target="_blank" rel="nofollow">$81</a>). It’s priced similarly to the Fujifilm QuickSnap Flash 400 (<a href="https://www.amazon.com/Fujifilm-QuickSnap-Flash-One-Time-Use-Camera/dp/B0F25C5257/" target="_blank" rel="nofollow">$21</a>) and the Kodak FunSaver 35mm (<a href="https://www.amazon.com/Kodak-FunSaver-35mm-Single-Camera/dp/B00001R3W3/" target="_blank" rel="nofollow">$29</a>) but they’re single-use cameras, so you can only use them for one roll of film. The Simple Use is, then, excellent value for money.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-design-build-quality"><span>Lomography Simple Use Reloadable Film Camera Classicolor review: Design & build 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="r58cqNTa9uA3fc64ox3CGb" name="Lomo_Simple_Use-2" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/r58cqNTa9uA3fc64ox3CGb.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 want a compact film camera that doesn’t take up much space in your handbag, the Lomography Simple Use is the one. The camera measures just 4.5 x 2.4 x 1.3 inches and weighs 4.23oz (including film and battery), so it’s similar to other film cameras, like the Kodak Ultra F9 and the Kodak Ektar H35. I’ve had no trouble carrying the Simple Use in my trousers’ pocket.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="xfu3eN4xWuVVk2Pm6QQu3h" name="Lomography_ 1.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/xfu3eN4xWuVVk2Pm6QQu3h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Simple Use comes in a few different colors, such as yellow, red, purple, orange, and turquoise. Each model is wrapped in stickers around the front and the back, with usage instructions — handy if this is your first time using a film camera. I tested the yellow model which looks fine. The aesthetics aren’t anything to write home about, and neither is the build quality.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5172px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="zsk9rxBd4hkYx2Fqi7vu3c" name="Lomo-Simple-Use-2" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/zsk9rxBd4hkYx2Fqi7vu3c.jpg" mos="" align="middle" fullscreen="" width="5172" height="2909" 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>Annoyingly, the Simple Use is made entirely of ABS plastic which feels cheap and flimsy. I feel like if I exerted even a bit of pressure on the film door’s hinges, it would fall apart. But you have to remind yourself that this camera costs just under $30. If build quality is a priority for you, I’d recommend the Kodak Ultra F9 instead — but you’ll need to spend an extra $20 for it.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-lens-viewfinder"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="FYFf3bLPShu2NjDbWYH46h" name="Lomography_ 7.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/FYFf3bLPShu2NjDbWYH46h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lomography Simple Use is fitted with a 31mm fixed-focus lens. This provides a wild field of view which means you can capture a lot of the scene, making the Simple Use great for street, landscapes, travel and everyday photography. The camera has a focusing range of 1m to ∞, so any subject closer than 1 meter will be blurry and out of 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:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="fHHVhw9Yh6Xi4j9mBpRC7h" name="Lomography_ 5.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/fHHVhw9Yh6Xi4j9mBpRC7h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>As a no-frills film camera, you don’t get any manual control over focus settings, so the Simple Use is excellent for beginners who just want to capture memories without thinking too much.</p><p>The Simple Use features a small viewfinder that I found easy to look through, even as someone who wears prescription glasses.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-controls"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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:6862px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="omPxyPXMdssawQtDfc3UHg" name="Lomo-Simple-Use" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/omPxyPXMdssawQtDfc3UHg.jpg" mos="" align="middle" fullscreen="" width="6862" height="3860" 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>Lomography hit the nail on the head when it called this camera Simple Use, because it is extremely simple to use. It comes pre-loaded with film, so is ready to use as soon as you unbox it. </p><p>All the shooting settings are adjusted automatically so all you need to do is look through the viewfinder, frame your subject, and press the shutter button to take a photo. I bet my family’s Golden Retriever, Odie, could use the Simple Use too… if he had opposable thumbs.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="He6FCTpFLbg5rLTXugAh9h" name="Lomography_ 4.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/He6FCTpFLbg5rLTXugAh9h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The film advance dial is located right below the shutter button, so it’s easy to advance the film to the next frame. Unloading the film is extremely straightforward, too. You’ll know the roll has finished when the film advance dial keeps spinning. After that, press the film door button to open the door, and take your roll out. Frustratingly, though, the film door button is quite stiff and takes a couple of tries before you can push it to open the door.</p><p>Since this is a reusable camera, you can pop in a new roll of film, close the door, and you’re ready to start shooting again.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="deJxcRjindAxauh8uXx97h" name="Lomography_ 6.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/deJxcRjindAxauh8uXx97h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Simple Use features a built-in flash too. To use it, you need to hold down the flash button located on the front of the camera, and take a photo while keeping it pressed.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="i8QE7mpJ6DTnQrpyfr2K6h" name="Lomography_ 2.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/i8QE7mpJ6DTnQrpyfr2K6h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></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 set of yellow, cyan and magenta colored gel filters attached next to the viewfinder, so you can move one (or all of them!) over the flash to create different color effects.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-image-quality"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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="yRn7jq8UYNsunAX5RRPj9b" name="Lomo_Simple_Use" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/yRn7jq8UYNsunAX5RRPj9b.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 Lomography Simple Use captures quintessentially retro-style images which are lo-fi and grainy — something that’s right up Gen Z’s street as retro tech has made a huge comeback these last few years. The images aren’t the most hi-res and are fit for sharing on social media. Not being able to see the photos until you get your film developed lets you stay in the moment and enjoy yourself, too.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/VN2gxMBpuToZrb3oYvkwCT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eq6nphYqQW9FvLvUfxaYDT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/hCJ6Y7yh8yY56Zb89DANMT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/fpa6VkWp6sp5NCon3siHQV.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/6Pkdv3QagHY2vpeQHgNwLT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/SE7jDL8tb39AbsZcfRmUpW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/P7kDJ5k6ML27bCds9zu3uW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zAx7oCmyTCyFjiRaLr7MCT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>And I’ve thoroughly enjoyed using the Simple Use to photograph Bristol Pride, the city of Bath in the U.K., and my travels. Results can be hit-or-miss but that’s the charm of film cameras. I’ve been happy with most of the photos I’ve taken with the Simple Use, and you can see a few examples above. I love how fluffy and well-defined the clouds look in the first and second shots, and the plane’s red jet engine really pops.</p><p>Most of the images are quite sharp, too. In the third, fourth and fifth shots, the edges of the plaque and the buildings are well-defined. In terms of colors, the blues in the Union Jack flags and the West Country Ales plaque appear deep, while the buildings’ beige stone looks as textured as in real life.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/wCa4LQDZzkeVETpfVGAbDT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/WBvxgkKVNtBTJ6AViQHrLT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/y4QpQF9KjhdmRFmumTYMHT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/BcSprSSZmuc7ciBYqmUmET.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/c3nyHLUUuXKaH2CBXa33DT.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>In the first two shots, taken on a sunny day, the colors look great. Each individual shade in the ‘Love Wins’ fan is discernible, as are the colors in the rainbow face paint. The third photo was taken on an overcast day but the image still looks pleasing to the eye.</p><p>There is some yellow and purple color fringing, also known as chromatic aberration, around the edges of the tent in the first shot, the trees in the fourth image and the chimneys and roof in the last photo. This is usually a sign of poor optics, but in a camera this cheap it’s not surprising, nor a huge issue. This also occurred with similarly-priced cameras like the Kodak F9.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/K3BigB3W4w5jrfqAje6uSS.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/VKycHVe5RLGW5QUHSvHZrW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/zc6EDEMeaVPAw4gW6xNduW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption>Red color filter.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/svLjvxrpWKWSFUFNkWKzqW.jpg" alt="A photo taken on a Lomography Simple Use Reloadable Film Camera Classicolor film camera" /><figcaption>Magenta + cyan color filters.<small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>The Simple Use’s shutter speed is limited to 1/120s and it’s got a fixed f/9 aperture, just like on the Kodak Ultra F9. The fixed f/9 aperture lets less light in than a wide aperture lens, like f/2.8 on the much pricier <a href="https://www.tomsguide.com/cameras-photography/lomography-lomo-mc-a-35-mm-film-camera-review">Lomography Lomo MC-A 35mm</a> ($549). This means that images shot in dimly lit environments turn out darker and potentially noisier, as you can see in the second shot above.</p><p>But you can salvage some of the shots with the built-in flash. The Simple Use’s built-in flash is fairly powerful, too, and it worked well to illuminate all the shots in the gallery above. The last two photos were taken with the color filters covering the flash, and they do what they’re designed to.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-film-cost-yield"><span>Lomography Simple Use Reloadable Film Camera Classicolor review: Film cost & yield</span></h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="PVWfRZUR7mEk8ETVdZSd5h" name="Lomography_ 3.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/PVWfRZUR7mEk8ETVdZSd5h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>The Lomography Simple Use comes pre-loaded with a 27-exposure roll of film, and subsequent rolls of 35mm film aren’t very expensive either. Film costs naturally vary depending on the type you get and where you live. For example, I live in the U.K. and used a 36-exposure roll of Fujifilm 200, which generally costs <a href="https://www.amazon.com/Fujifilm-Fujicolor-Color-Negative-Film/dp/B0DHFFTTTZ/" target="_blank" rel="nofollow">$31</a> / <a href="https://www.amazon.co.uk/Fujifilm-Fujicolor-Negative-Exposures-3-Pack/dp/B00JDOZB6C/" target="_blank" rel="nofollow">£39</a> for a pack of three.</p><p>Film development costs will vary too. My film development cost me £18 for one roll of film (digital scans only). But I asked an American colleague about film developing costs in the U.S., and he said that it can cost as little as $10.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-battery-life"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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="SDjxJuCKtqFfQvMT9fqT5h" name="Lomography_ 8.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/SDjxJuCKtqFfQvMT9fqT5h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>Similar to most other film cameras I’ve tested, like the Kodak Ultra F9, the Lomography Simple Use utilizes a single AA battery (included in the box). These are easily sourced and you probably have a couple spares lying around your house, too.</p><p>Lomography doesn’t specify how long an AA battery lasts as it’s primarily there to power the flash, but I was able to get through two rolls of film comfortably while occasionally using the flash.</p><h2 class="article-body__section" id="section-lomography-simple-use-reloadable-film-camera-classicolor-review-verdict"><span>Lomography Simple Use Reloadable Film Camera Classicolor 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:838px;"><p class="vanilla-image-block" style="padding-top:56.32%;"><img id="6godXDrFmrymYCQ7hrZa6h" name="Lomography_.JPG" alt="A yellow Lomography Simple Use Reloadable Film Camera Classicolor film camera" src="https://cdn.mos.cms.futurecdn.net/v2/t:193,l:194,cw:838,ch:472,q:80/6godXDrFmrymYCQ7hrZa6h.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>If you want a cheap and very cheerful film camera, you can’t go wrong with the Lomography Simple Use. As the name suggests, this is an extremely easy camera to use. It’s perfect for beginners, casual users, and anyone who wants to capture retro-style photos — albeit with slight chromatic aberration (which doesn’t necessarily ruin the images).</p><p>The Simple Use is great value for money as it costs just $27 and comes pre-loaded with a 27-exposure film roll as well as an AA battery to power the flash. It’s fitted with a set of colored filters too to help you get creative shots, too.</p><p>Though build quality isn’t anything to write home about and removing film can be slightly frustrating, those are minor issues at this price point. At the end of the day, the Simple Use is fun and it makes capturing memories easy while allowing you to stay in the moment.</p>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ My 13-year-old Nikon DSLR has been battered, bruised, beaten and survived it all — it’s what made me fall in love with photography ]]></title>
                                                                                                <dc:content><![CDATA[ <p>As the old adage goes: the best camera is the one in your hands. It's something I've been told since I was a child, playing with my dad's bulky DSLR, not really knowing what I was doing with it. Now, as an adult and a tech journalist, I review the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a> day in and out, and it's wonderful the leaps and bounds these nifty gadgets have come.</p><p>After reviewing the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-t50-review">Fujifilm X-T50</a> all the way back in June 2024, I'm now the proud owner of one, and I've taken many amazing photos on it. But like they say: you never forget your first, and my first "proper" camera was the Nikon D5100 that my parents bought me when I was 16.</p><p>The D5100's specs aren't much to write home about by today's standards. A 16.2MP CMOS sensor, no stabilization, an 11-point autofocus system, 1080P/30fps video, an optical viewfinder, and a 921,000-dot non-touch LCD monitor. Not the greatest specs, but frankly, I don't care. The D5100 is the camera that made me fall in love with photography, and to this day, 13 years later, I still find use in it.</p><h2 id="let-me-take-you-back-to-2013">Let me take you back to 2013</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6832px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9xkkwCqqasQTWj9DNYZ3Zg" name="Nikon_D5100-3" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/9xkkwCqqasQTWj9DNYZ3Zg.jpg" mos="" align="middle" fullscreen="" width="6832" height="3843" 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>2013. It feels like such a long time ago and yet, I vividly remember when I first held the Nikon D5100 in my hands. I was in an all-girls' boarding school, and we were about to go on our annual school trip to the Andaman and Nicobar Islands in India. I remember my classmates showing off their Canon EOS 5D Mark IIIs, and I won't lie, I felt a pang of jealousy. I wanted a "proper" camera too, so I floated the idea to my parents.</p><p>My parents rarely said no to things I desperately wanted, within reason, and the kind people they still are, they agreed to take me to a camera shop to pick up my first camera. The salesman tried selling us on the most expensive cameras, but we (and by that I mean my parents) naturally had a budget in mind, and we settled on the D5100, which back in the day must have cost around $800 for the body only — and in Indian Rupees, it was <em>a lot.</em> My parents still bought it for me, along with an 18-55mm lens.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6550px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="LT9oXim9CgQB8pNXFEKZ8g" name="Nikon_D5100-2" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/LT9oXim9CgQB8pNXFEKZ8g.jpg" mos="" align="middle" fullscreen="" width="6550" height="3684" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><div><blockquote><p>I thought I was a magician capturing what I considered were award-winning images — they were not, but in that moment, it didn't matter, because I was having fun.</p></blockquote></div><p>I must admit, my photography knowledge back in the day was nowhere near as polished as it is today, so "18-55mm" meant nothing to me. All I knew was that I now had a camera that could take good photos of my friends, and the beautiful Andaman Sea and Bay of Bengal. I thought I was a magician capturing what I considered were award-winning images — they were not, but in that moment, it didn't matter, because I was having fun. Photography is meant to be fun, and that's the most important lesson the D5100 has taught me.</p><h2 id="back-to-the-present">Back to the present</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3024px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kYg7J4pWEHvA5c4J9pEAsc" name="Nikon_D5100" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/kYg7J4pWEHvA5c4J9pEAsc.jpg" mos="" align="middle" fullscreen="" width="3024" height="1701" 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>Years went by and the Nikon D5100 remained a constant in my life. I took it with me to my undergraduate university in India, and brought it with me when I first moved to the U.K. for postgrad in 2018. The D5100 has been tossed around in luggage, thrown around on beaches, and it has been heavily used in all kinds of weather — including the rain.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4947px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Fxok3us38Nz2BgztNkrUGf" name="Nikon_D5100-8" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/Fxok3us38Nz2BgztNkrUGf.jpg" mos="" align="middle" fullscreen="" width="4947" height="2783" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><div><blockquote><p>I remember pulling out my D5100 at my first-ever press event in 2024 (for a Fujifilm instant camera), and one of the other journalists there remarked, "Oh, you're proper old school."</p></blockquote></div><p>And it has survived. Battered, bruised, beaten, my 13-year-old D5100 still works, and up until I bought the Fujifilm X-T50, I used it on vacations, at work for product photography, and my partner used it as her primary camera for her art and video work. I remember pulling out my D5100 at my first-ever press event in 2024 (for a Fujifilm instant camera), and one of the other journalists there remarked, "Oh, you're proper old school." Every time I saw him at press events after, he'd go, "Still rocking the DSLR, eh?" with a smile on his face.</p><p>I think there's a lot to love about DSLRs, which is why I'm here, talking about my love for the D5100. When we think of DSLRs, we think they're bulky, slow, and a pain to use, especially when compared to fast mirrorless cameras that, like the Fujifilm X-T50, enable you to burst-shoot at 30fps. I'd argue that a DSLR can make you a better, more considerate photographer, and it's not always about having the latest tech in your hand.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OoDNoX"></div>                            </div>                            <script src="https://kwizly.com/embed/OoDNoX.js" async></script><h2 id="why-i-still-use-and-love-my-d5100">Why I still use (and love) my D5100</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/WrbvPa6zuqyNxBGJjZHBYR.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZiAFtUy3Bw5cm9DPtsYkYa.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mdfBviLEosgoGdEi8MMhWR.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/W9ZXyMKQmHksEQwJ8RVeWR.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9CbXWVzBybd4gJzUarmbYR.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Would you like to know why I still use and love my Nikon D5100? The photos in the gallery above were taken by my partner in Iceland. That's why. The D5100 is a capable and competent camera that takes beautiful photos — yes, even though it packs a 16.2MP CMOS sensor only. When my partner and I decided to take a trip to Iceland, I packed the Fujifilm X-T50 and she packed the D5100 — and the photos she took on the D5100 are nothing short of gorgeous (it, of course, helps that we were in a country best described as heaven on earth).</p><div><blockquote><p>When I asked my partner why she enjoys using the D5100, her answer was simple: 'It's a camera that takes good photos, and I like taking my time with it.'</p></blockquote></div><p>When I asked my partner why she enjoys using the D5100, her answer was simple: "It's a camera that takes good photos, and I like taking my time with it." I smiled when she said that, because it further proves that the best camera is the one in your hands, and that it's important to take your time with framing your shots. Sure, she had her 50MP <a href="https://www.tomsguide.com/phones/google-pixel-phones/google-pixel-9-review">Google Pixel 9</a> on the trip too, but she still opted for the D5100. Though the D5100 can't compete with the stupidly good autofocus systems on today's mirrorless cameras, it still proved quick enough on our Iceland trip.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/gbHFjnmqc3YUpx4b3AW4mb.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Jax6Z4o2fFMk3RkitqneGZ.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/E6fAAdjqKjyjDaSmPszXwc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/XqUHnCE885xsaqXTAeThxc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JJwnh2HPjVG5jB8kZcRHvc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jcsC7MtdUUYtTXjJAJvMCd.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mPAutBzfDjrTy3GgAwzeKb.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JaynzcG3ea9paRkotsxuzc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/NKzWWXL23J6sLSsNXP9Ptc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/GyVmGKQm5kDtgE2iSEGraY.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>What I love most about the D5100 is the colors it captures. Though I prefer Canon's and Fujifilm's color science now, the D5100 hasn't given me much to complain about. In the gallery earlier, I love that the bright highlights, seen in the waves crashing in the first slide, don't appear blown out, and the dark areas, such as the black sand on Reynisfjara beach, aren't grainy or noisy.</p><p>Similarly, in the gallery above, the colors have come out lovely and true to life. The deep blues in the sky, the vivid greens, the popping shades of pink and purple — they all look refined and pleasing. The photos are fairly sharp too, with each individual feather on the duck and pigeon (mostly) discernible.</p><h2 id="the-photographer-that-i-am-today">The photographer that I am today</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4164px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="csT88vATZqh4zmVvjntXtd" name="Nikon_D5100-6" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/csT88vATZqh4zmVvjntXtd.jpg" mos="" align="middle" fullscreen="" width="4164" height="2342" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><div><blockquote><p>They say clothes maketh the man, and by that logic, the camera maketh the photographer. I don't believe that's true.</p></blockquote></div><p>Fast forward to today. I'm a senior reviews writer testing the best tech day in and out. I've tested super speedy Canons, Sonys, Fujis, and even Leicas. But a Nikon started it all. I don't think I'm an extraordinary photographer — but I take photos of things that look pleasing to me, to immortalize memories, and I learn as I go.</p><p>They say clothes maketh the man, and by that logic, the camera maketh the photographer. I don't believe that's true. Your desire to take good photos, and your ability to learn and adapt are what make you a good photographer. I think it's great that mirrorless cameras are accessible, and it makes me happy that people younger than me are taking up photography. But I'd recommend everyone to try shooting on a DSLR at least once. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5953px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="LuhRuAs6WtmWQbcfEpTBgf" name="Nikon_D5100-5" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/LuhRuAs6WtmWQbcfEpTBgf.jpg" mos="" align="middle" fullscreen="" width="5953" height="3349" 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>With the Nikon D5100 and other DSLRs which feature optical viewfinders (OVFs) instead of electronic ones (EVFs) on modern cameras, I've learned that relying on your gut instinct is very important. The only information you get in your viewfinder is basic shooting settings, like shutter speed and ISO in the margins.</p><p>There are no live histograms, digital levelling gauges, zebra lines for overexposure, or focus peaking. It's just you and your subject. When you adjust shutter speed or ISO, the change isn't reflected in the viewfinder; the OVF shows a real-life reflection rather than a digital preview, so I've had to learn how to read light and manually calculate exposure.</p><p>And therein lies the magic. I love shooting with my Fujifilm X-T50 and it's one of the best things I've ever bought, but I also enjoy the simplicity of my D5100. With the latter, it's just me and the camera, and I feel like if my parents hadn't bought the D5100 for me, I wouldn't be where or who I am today. It changed the trajectory of my life.</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/my-favorite-cameras-drones-of-2026-so-far">I’ve tested a ton of cameras and drones in 2026 — and these are my 5 favorites (so far) from DJI, Canon, Leica and more</a></li><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/cameras-photography/mirrorless-cameras/my-favorite-shots-taken-on-the-canon-eos-r6-mark-iii">I’ve taken over 6,000 photos on the Canon EOS R6 Mark III — here are my favorite portraits, wildlife, and landscape shots</a></li></ul> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/a-love-letter-to-my-13-year-old-nikon-dslr</link>
                                                                            <description>
                            <![CDATA[ I've owned the Nikon D5100 since 2013, and it's the camera that made me the photographer I am today. This is a love letter to the camera that started it all. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">DZn5vUmmiBTtipFUU64Jg</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/xrXMRiNu5MSS8eQdb93oaa-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Wed, 29 Jul 2026 10:15:00 +0000</pubDate>                                                                                                                                <updated>Thu, 30 Jul 2026 14:27:28 +0000</updated>
                                                                                                                                            <category><![CDATA[DSLR Cameras]]></category>
                                                    <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/xrXMRiNu5MSS8eQdb93oaa-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A person holding a Nikon D5100 DSLR camera]]></media:description>                                                            <media:text><![CDATA[A person holding a Nikon D5100 DSLR camera]]></media:text>
                                <media:title type="plain"><![CDATA[A person holding a Nikon D5100 DSLR camera]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/xrXMRiNu5MSS8eQdb93oaa-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>As the old adage goes: the best camera is the one in your hands. It's something I've been told since I was a child, playing with my dad's bulky DSLR, not really knowing what I was doing with it. Now, as an adult and a tech journalist, I review the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless cameras</a> day in and out, and it's wonderful the leaps and bounds these nifty gadgets have come.</p><p>After reviewing the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-x-t50-review">Fujifilm X-T50</a> all the way back in June 2024, I'm now the proud owner of one, and I've taken many amazing photos on it. But like they say: you never forget your first, and my first "proper" camera was the Nikon D5100 that my parents bought me when I was 16.</p><p>The D5100's specs aren't much to write home about by today's standards. A 16.2MP CMOS sensor, no stabilization, an 11-point autofocus system, 1080P/30fps video, an optical viewfinder, and a 921,000-dot non-touch LCD monitor. Not the greatest specs, but frankly, I don't care. The D5100 is the camera that made me fall in love with photography, and to this day, 13 years later, I still find use in it.</p><h2 id="let-me-take-you-back-to-2013">Let me take you back to 2013</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6832px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9xkkwCqqasQTWj9DNYZ3Zg" name="Nikon_D5100-3" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/9xkkwCqqasQTWj9DNYZ3Zg.jpg" mos="" align="middle" fullscreen="" width="6832" height="3843" 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>2013. It feels like such a long time ago and yet, I vividly remember when I first held the Nikon D5100 in my hands. I was in an all-girls' boarding school, and we were about to go on our annual school trip to the Andaman and Nicobar Islands in India. I remember my classmates showing off their Canon EOS 5D Mark IIIs, and I won't lie, I felt a pang of jealousy. I wanted a "proper" camera too, so I floated the idea to my parents.</p><p>My parents rarely said no to things I desperately wanted, within reason, and the kind people they still are, they agreed to take me to a camera shop to pick up my first camera. The salesman tried selling us on the most expensive cameras, but we (and by that I mean my parents) naturally had a budget in mind, and we settled on the D5100, which back in the day must have cost around $800 for the body only — and in Indian Rupees, it was <em>a lot.</em> My parents still bought it for me, along with an 18-55mm lens.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:6550px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="LT9oXim9CgQB8pNXFEKZ8g" name="Nikon_D5100-2" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/LT9oXim9CgQB8pNXFEKZ8g.jpg" mos="" align="middle" fullscreen="" width="6550" height="3684" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><div><blockquote><p>I thought I was a magician capturing what I considered were award-winning images — they were not, but in that moment, it didn't matter, because I was having fun.</p></blockquote></div><p>I must admit, my photography knowledge back in the day was nowhere near as polished as it is today, so "18-55mm" meant nothing to me. All I knew was that I now had a camera that could take good photos of my friends, and the beautiful Andaman Sea and Bay of Bengal. I thought I was a magician capturing what I considered were award-winning images — they were not, but in that moment, it didn't matter, because I was having fun. Photography is meant to be fun, and that's the most important lesson the D5100 has taught me.</p><h2 id="back-to-the-present">Back to the present</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:3024px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="kYg7J4pWEHvA5c4J9pEAsc" name="Nikon_D5100" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/kYg7J4pWEHvA5c4J9pEAsc.jpg" mos="" align="middle" fullscreen="" width="3024" height="1701" 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>Years went by and the Nikon D5100 remained a constant in my life. I took it with me to my undergraduate university in India, and brought it with me when I first moved to the U.K. for postgrad in 2018. The D5100 has been tossed around in luggage, thrown around on beaches, and it has been heavily used in all kinds of weather — including the rain.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4947px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="Fxok3us38Nz2BgztNkrUGf" name="Nikon_D5100-8" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/Fxok3us38Nz2BgztNkrUGf.jpg" mos="" align="middle" fullscreen="" width="4947" height="2783" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><div><blockquote><p>I remember pulling out my D5100 at my first-ever press event in 2024 (for a Fujifilm instant camera), and one of the other journalists there remarked, "Oh, you're proper old school."</p></blockquote></div><p>And it has survived. Battered, bruised, beaten, my 13-year-old D5100 still works, and up until I bought the Fujifilm X-T50, I used it on vacations, at work for product photography, and my partner used it as her primary camera for her art and video work. I remember pulling out my D5100 at my first-ever press event in 2024 (for a Fujifilm instant camera), and one of the other journalists there remarked, "Oh, you're proper old school." Every time I saw him at press events after, he'd go, "Still rocking the DSLR, eh?" with a smile on his face.</p><p>I think there's a lot to love about DSLRs, which is why I'm here, talking about my love for the D5100. When we think of DSLRs, we think they're bulky, slow, and a pain to use, especially when compared to fast mirrorless cameras that, like the Fujifilm X-T50, enable you to burst-shoot at 30fps. I'd argue that a DSLR can make you a better, more considerate photographer, and it's not always about having the latest tech in your hand.</p><div style="min-height: 250px;">                                <div class="kwizly-quiz kwizly-OoDNoX"></div>                            </div>                            <script src="https://kwizly.com/embed/OoDNoX.js" async></script><h2 id="why-i-still-use-and-love-my-d5100">Why I still use (and love) my D5100</h2><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/WrbvPa6zuqyNxBGJjZHBYR.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZiAFtUy3Bw5cm9DPtsYkYa.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mdfBviLEosgoGdEi8MMhWR.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/W9ZXyMKQmHksEQwJ8RVeWR.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/9CbXWVzBybd4gJzUarmbYR.jpg" alt="A photo of a person taken in Iceland on the Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Would you like to know why I still use and love my Nikon D5100? The photos in the gallery above were taken by my partner in Iceland. That's why. The D5100 is a capable and competent camera that takes beautiful photos — yes, even though it packs a 16.2MP CMOS sensor only. When my partner and I decided to take a trip to Iceland, I packed the Fujifilm X-T50 and she packed the D5100 — and the photos she took on the D5100 are nothing short of gorgeous (it, of course, helps that we were in a country best described as heaven on earth).</p><div><blockquote><p>When I asked my partner why she enjoys using the D5100, her answer was simple: 'It's a camera that takes good photos, and I like taking my time with it.'</p></blockquote></div><p>When I asked my partner why she enjoys using the D5100, her answer was simple: "It's a camera that takes good photos, and I like taking my time with it." I smiled when she said that, because it further proves that the best camera is the one in your hands, and that it's important to take your time with framing your shots. Sure, she had her 50MP <a href="https://www.tomsguide.com/phones/google-pixel-phones/google-pixel-9-review">Google Pixel 9</a> on the trip too, but she still opted for the D5100. Though the D5100 can't compete with the stupidly good autofocus systems on today's mirrorless cameras, it still proved quick enough on our Iceland trip.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/gbHFjnmqc3YUpx4b3AW4mb.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/Jax6Z4o2fFMk3RkitqneGZ.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/E6fAAdjqKjyjDaSmPszXwc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/XqUHnCE885xsaqXTAeThxc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JJwnh2HPjVG5jB8kZcRHvc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/jcsC7MtdUUYtTXjJAJvMCd.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/mPAutBzfDjrTy3GgAwzeKb.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/JaynzcG3ea9paRkotsxuzc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/NKzWWXL23J6sLSsNXP9Ptc.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/GyVmGKQm5kDtgE2iSEGraY.jpg" alt="A photo taken on a Nikon D5100 DSLR camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>What I love most about the D5100 is the colors it captures. Though I prefer Canon's and Fujifilm's color science now, the D5100 hasn't given me much to complain about. In the gallery earlier, I love that the bright highlights, seen in the waves crashing in the first slide, don't appear blown out, and the dark areas, such as the black sand on Reynisfjara beach, aren't grainy or noisy.</p><p>Similarly, in the gallery above, the colors have come out lovely and true to life. The deep blues in the sky, the vivid greens, the popping shades of pink and purple — they all look refined and pleasing. The photos are fairly sharp too, with each individual feather on the duck and pigeon (mostly) discernible.</p><h2 id="the-photographer-that-i-am-today">The photographer that I am today</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:4164px;"><p class="vanilla-image-block" style="padding-top:56.24%;"><img id="csT88vATZqh4zmVvjntXtd" name="Nikon_D5100-6" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/csT88vATZqh4zmVvjntXtd.jpg" mos="" align="middle" fullscreen="" width="4164" height="2342" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Nikita Achanta / Tom's Guide)</span></figcaption></figure><div><blockquote><p>They say clothes maketh the man, and by that logic, the camera maketh the photographer. I don't believe that's true.</p></blockquote></div><p>Fast forward to today. I'm a senior reviews writer testing the best tech day in and out. I've tested super speedy Canons, Sonys, Fujis, and even Leicas. But a Nikon started it all. I don't think I'm an extraordinary photographer — but I take photos of things that look pleasing to me, to immortalize memories, and I learn as I go.</p><p>They say clothes maketh the man, and by that logic, the camera maketh the photographer. I don't believe that's true. Your desire to take good photos, and your ability to learn and adapt are what make you a good photographer. I think it's great that mirrorless cameras are accessible, and it makes me happy that people younger than me are taking up photography. But I'd recommend everyone to try shooting on a DSLR at least once. </p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:5953px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="LuhRuAs6WtmWQbcfEpTBgf" name="Nikon_D5100-5" alt="A person holding a Nikon D5100 DSLR camera" src="https://cdn.mos.cms.futurecdn.net/LuhRuAs6WtmWQbcfEpTBgf.jpg" mos="" align="middle" fullscreen="" width="5953" height="3349" 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>With the Nikon D5100 and other DSLRs which feature optical viewfinders (OVFs) instead of electronic ones (EVFs) on modern cameras, I've learned that relying on your gut instinct is very important. The only information you get in your viewfinder is basic shooting settings, like shutter speed and ISO in the margins.</p><p>There are no live histograms, digital levelling gauges, zebra lines for overexposure, or focus peaking. It's just you and your subject. When you adjust shutter speed or ISO, the change isn't reflected in the viewfinder; the OVF shows a real-life reflection rather than a digital preview, so I've had to learn how to read light and manually calculate exposure.</p><p>And therein lies the magic. I love shooting with my Fujifilm X-T50 and it's one of the best things I've ever bought, but I also enjoy the simplicity of my D5100. With the latter, it's just me and the camera, and I feel like if my parents hadn't bought the D5100 for me, I wouldn't be where or who I am today. It changed the trajectory of my life.</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/my-favorite-cameras-drones-of-2026-so-far">I’ve tested a ton of cameras and drones in 2026 — and these are my 5 favorites (so far) from DJI, Canon, Leica and more</a></li><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/cameras-photography/mirrorless-cameras/my-favorite-shots-taken-on-the-canon-eos-r6-mark-iii">I’ve taken over 6,000 photos on the Canon EOS R6 Mark III — here are my favorite portraits, wildlife, and landscape shots</a></li></ul>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ I’ve tested a ton of cameras and drones in 2026 — and these are my 5 favorites (so far) from DJI, Canon, Leica and more ]]></title>
                                                                                                <dc:content><![CDATA[ <p>Every time the half-year mark comes around, I think to myself, "This has been a great year for cameras." It happened last year and the year before, and it has happened again this year. So far in 2026, we've been treated to some fantastic cameras and drones, and brands continue to prove that there's still room to grow and develop in terms of imaging tech.</p><p>If you don't know me, my name is Nikita and I'm a Senior Reviews Writer, testing the <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">best drones</a> and the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras</a>. This year, I've tested 12 of these nifty gadgets so far — from keychain cameras like the <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a> and black-and-white snappers like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-q3-monochrom-review">Leica Q3 Monochrom</a>, to speedy mirrorless beasts like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">Canon EOS R6 Mark III</a> and aerial wonders like the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-avata-360-review">DJI Avata 360</a>.</p><p>After a somewhat slow start to the year, we've been spoiled thanks to new launches from the likes of Leica, and DJI coming out with a range of drones and gimbal cameras. Now that we're well into July, I thought I'd round up my top five cameras and drones of 2026 so far. Has there been a camera or drone that's been a standout for you? I'd love to hear from you in the comments!</p><h2 id="5-kodak-charmera">5. Kodak Charmera</h2><p><strong>Just a no-frills, fun keychain camera</strong><br><em>My rating: </em>★★★★☆</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6saMUxA9kPbPgFgeWW28PG" name="Kodak-Charmera-op-ed-7" alt="A photo of a Kodak Charmera keychain camera" src="https://cdn.mos.cms.futurecdn.net/6saMUxA9kPbPgFgeWW28PG.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" class="inline"><img id="WqLzPy4iPS8qbMqK42dYLY" class="endorsement-img endorsement-bottom-right" style="max-width: 100px; max-height: 100px;" src="https://cdn.mos.cms.futurecdn.net/WqLzPy4iPS8qbMqK42dYLY.png" name="TG_badges_Recommended.png" alt="Tom's Guide Recommended product badge"></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 was putting this list together, I knew the Kodak Charmera was going to be included. If there's one thing 2026 has shown us already it's that cameras can be whimsical and just pure fun — and the Charmera proves it. You may have seen this nifty little keychain camera on TikTok or Instagram. It went viral earlier this year for its compact form factor and oh-so little asking price, and if you need a no-frills camera for a night out, this is the one to get.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/CVv9bGrVysbZQaXnVbH7e.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/7Cx73wSeWvkBVmo3gkkHe.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eKe3jankij69jYdFfHeFf.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/kVABmfTeRPEeNn4oGBxji.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/WV7mFBFQGiyC7tAe9HwdmF.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QJaxTurnLxgSWUhLQL5mfF.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/epp7DtZGJBrRdk66U4aJaF.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZPjnnhLUJw337C4KfG6yaF.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Though the camera is fitted with a 1.6MP 1/4-inch CMOS sensor that doesn't take award-winning images, the Charmera is all about fun. The charm lies in its lo-fi images that remind me of the photos in my family's photo albums. What makes the Charmera unique is its extremely portable body — clip it on to your keys or your bag and you're good to go.</p><p>What I love most about the Charmera is its ease of use. Your 5-year-old niece who's just getting to grips with using her hands will be able to use it, as will your 60-year-old uncle who just wants to immortalize memories without caring much about image quality. Pass it around a table at a pub, or add it to the camera gear that's going to be at your wedding — the Charmera is for everyone.</p><p><em><strong>Read my full </strong></em><a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review"><em><strong>Kodak Charmera review</strong></em></a><em><strong>.</strong></em></p><div data-widget-type="review" data-model-name="Kodak Charmera"></div><h2 id="4-dji-osmo-pocket-4p">4. DJI Osmo Pocket 4P</h2><p><strong>DJI's best gimbal camera just got better</strong><br><em>My rating: Not scored yet</em></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width: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>The DJI Osmo Pocket 4P is perhaps one of the industry's worst kept secrets. Rumors have been circulating for months, so when the <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review">DJI Osmo Pocket 4</a> dropped, people wondered why it didn't have two dual lenses — and then the Osmo Pocket 4P dropped. Well, you can't buy it yet, but it's out there. I've held it; I've recorded footage on it in Malta; and I can tell you that it's very real. I can also tell you that this gimbal camera might give the Insta360 Luna Ultra a run for its money.</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/0RR3ZHn9PIs" allowfullscreen></iframe></div></div><p>But my lips are sealed due to embargoes and restrictions, and all I can really tell you about the Osmo Pocket 4P is this: it features D-Log2 support and a whopping 17 stops of dynamic range, and it comes in black and white. It's also got two lenses, one of which is a 60mm medium-telephoto.</p><p>I've spent a fair amount of time using the Osmo Pocket 4P, and it didn't take long for it to become one of my favorite cameras of 2026 so far. And why wouldn't it be? It builds on the success of the base Osmo Pocket 4 (which received a 5/5 rating from Senior Reviews Editor, Peter Wolinski). If you haven't bought the base model, I think it's worth waiting for the Osmo Pocket 4P. Trust me, I've been gobsmacked by it, and I'm sure you will be too.</p><p><em><strong>Read my </strong></em><a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4p-first-impressions"><em><strong>first impressions of the DJI Osmo Pocket 4P</strong></em></a><em><strong>.</strong></em></p><div data-widget-type="review" data-model-name="DJI Osmo Pocket 4"></div><h2 id="3-canon-eos-r6-mark-iii">3. Canon EOS R6 Mark III</h2><p><strong>Good becomes great</strong><br><em>My rating: </em>★★★★<strong>½</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1144px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="D7e7kySfUuavP3WUz4GNB7" name="EOSR6III" alt="A woman holding a Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/D7e7kySfUuavP3WUz4GNB7.jpg" mos="" align="middle" fullscreen="" width="1144" height="644" attribution="" class="inline"><img id="Xc987sPL6EDZGCrLfXakAb" class="endorsement-img endorsement-bottom-right" style="max-width: 100px; max-height: 100px;" src="https://cdn.mos.cms.futurecdn.net/Xc987sPL6EDZGCrLfXakAb.png" name="TG_ed_choice.png" alt="Editor's Choice"></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 succeeded the second-gen model in November 2025, but I tested the camera in 2026, so it counts! Good became great as Canon introduced substantial upgrades to its predecessor to deliver one of the best mid-range cameras. Having taken over 6,000 photos on the EOS R6 Mark III, I've been extremely impressed with its capabilities. The camera packs a 32.5MP full frame sensor that captures plenty of detail and gives you some room for making crops in post-production, too.</p><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/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/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/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/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/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/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><p>Combine that beautiful sensor with 40fps burst shooting and best-in-class autofocus, and you've got yourself a powerful camera for sports, wildlife, portraits, landscapes, you name it. As someone who loves photographing birds and animals, I appreciated the EOS R6 Mark III's ability to quickly lock on to subjects and capture stunning images. Canon's vaunted color science is the cherry on top of the cake.</p><p>And it's a delicious cake topped with many, many cherries. 7K Open Gate video and 4K/120fps video capabilities make the EOS R6 Mark III a competent hybrid camera. The addition of CFExpress support means the camera can accommodate 7K RAW files and sustain its rapid 40fps drive.</p><p><em><strong>Read my full </strong></em><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review"><em><strong>Canon EOS R6 Mark III review</strong></em></a><em><strong>.</strong></em></p><div data-widget-type="review" data-model-name="Canon EOS R6 Mark III"></div><h2 id="2-leica-sl3-p">2. Leica SL3-P</h2><p><strong>Finally taking the fight to Canon and Sony</strong><br><em>My rating: </em>★★★★<strong>½</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width: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="" class="inline"><img id="Xc987sPL6EDZGCrLfXakAb" class="endorsement-img endorsement-top-left" style="max-width: 100px; max-height: 100px;" src="https://cdn.mos.cms.futurecdn.net/Xc987sPL6EDZGCrLfXakAb.png" name="TG_ed_choice.png" alt="Editor's Choice"></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 a legacy brand and though it has always made excellent (albeit <em>very </em>expensive) cameras, it’s always struggled to compete with heavy hitters like Sony and Canon, in my opinion — until now. The Leica SL3-P is one of the finest mirrorless cameras I’ve ever tested, and certainly the best of 2026 so far. What makes the SL3-P a standout is its sublime handling, superb build quality (you'd expect nothing less at this price), and imaging prowess.</p><figure role="gallery"><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/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/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/VkwACTGDWLmpQqERZbQWaW.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/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/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><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/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><p>I was seriously impressed with its 40fps high-speed drive and best-in-class autofocus that ensures you never miss an action shot — and as a wildlife photographer, I love it. Throw in a 44MP sensor, 8K Open Gate video, strong dynamic range performance, and sublime handling, and you’ve got yourself a winner. Well done, Leica.</p><p>The SL3-P makes Leica a serious alternative to Canon and Sony, and if you think, "Nope, Leicas are way too expensive," you'd be wrong. Sony's flagship <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">A1 II </a>and Canon's <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r5-mark-ii-review">EOS R5 Mark II</a> bodies cost almost the same. I don't think Leica's price tag should scare you any longer — especially when you're getting a ridiculously sharp sensor.</p><p><em><strong>Read my full </strong></em><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-sl3-p-review"><em><strong>Leica SL3-P review.</strong></em></a></p><div data-widget-type="review" data-model-name="Leica SL3-P"></div><h2 id="1-dji-avata-360">1. DJI Avata 360</h2><p><strong>DJI claims its stake as the king of the skies</strong><br><em>My rating: </em>★★★★<strong>½</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="b4HLoC8e5MA5dACvMWYTcn" name="DJI_Avata_360" alt="A DJI Avata 360 360-degree drone" src="https://cdn.mos.cms.futurecdn.net/b4HLoC8e5MA5dACvMWYTcn.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" class="inline"><img id="Xc987sPL6EDZGCrLfXakAb" class="endorsement-img endorsement-bottom-right" style="max-width: 100px; max-height: 100px;" src="https://cdn.mos.cms.futurecdn.net/Xc987sPL6EDZGCrLfXakAb.png" name="TG_ed_choice.png" alt="Editor's Choice"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>When the <a href="https://www.tomsguide.com/cameras-photography/drones/antigravity-a1-review">Antigravity A1</a>, the world's first 360° drone came out last year, I thought to myself, "This is an excellent drone; I wonder what DJI's answer is going to be." And DJI's answer was simple: the Avata 360 did everything the Antigravity A1 does but better, and at a much lower price. It undercut its primary rival, and DJI delivered a 360° drone that doesn't give you much to complain about.</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/DtyupaS6iLc" allowfullscreen></iframe></div></div><p>I absolutely adore the Avata 360, and as someone who has spent nearly a thousand hours flying, testing, writing and reading about drones, it's one of the best today, period. Fitted with twin lenses that capture every angle in 8K at 60fps without any stitching lines, and 120MP hi-res stills, the Avata 360 is a formidable content creation tool.</p><p>Exceptional build quality, dual ways to fly (FPV and with a standard controller), user-friendly software for editing, you name it: the Avata 360 does it all. Out of all the cameras and drones I've tested this year so far, this is the one I come back to time and again. DJI, despite <a href="https://www.tomsguide.com/cameras-photography/drones/us-drone-ban-update">facing a ban on its drones and enhanced scrutiny in the U.S.</a>, shows no signs of stopping with its Avata 360, Lito X1 and Lito 1 quadcopters, and I can't wait to see what else the Chinese giant has up its sleeve going forward.</p><p><em><strong>Read my full </strong></em><a href="https://www.tomsguide.com/cameras-photography/drones/dji-avata-360-review"><em><strong>DJI Avata 360 review</strong></em></a><em><strong>.</strong></em></p><div data-widget-type="review" data-model-name="DJI Avata 360"></div><h2 id="there-s-more-where-that-came-from">There's more where that came from...</h2><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="48Awcj5xnebuk8DUcES6QK" name="Instax-mini-evo-cinema-hands-on" alt="A black Fujifilm Instax mini Evo Cinema hybrid instant camera" src="https://cdn.mos.cms.futurecdn.net/48Awcj5xnebuk8DUcES6QK.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, of course, other cameras I've tested that haven't made the list, but they still deserve a shoutout. The first honorable mention that springs to my mind is the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-evo-cinema-review">Fujifilm Instax mini Evo Cinema</a>, a hybrid instant camera that redefines everything we know about instant cameras.</p><p>It's one of a kind: it lets you shoot photos and print directly from your smartphone, and you can record video clips up to 15 seconds long, choose a frame, and print on Instax film with a QR code that plays said video on a smartphone. It's fun and it's different, and it earned a 4.5-star rating from me.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width: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>Then there's the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-13-review">Fujifilm Instax mini 13</a>, which takes things back to basics. It's an analog instant cameras that simply shoots and prints photos within 90 seconds. Image and print quality is great, with retro colors that remind us all why we fell in love with instant photography in the first place.</p><p>I also tested the <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-3-review">Potensic Atom 3</a>, a powerful DJI alternative that, for the first time on a Potensic drone, introduces 4K/60fps video and RAW images. At an affordable price tag, the Atom 3 packs up to 50 minutes of flight time and accurate AI subject tracking.</p><p>But that's all from me (for now). I hope you've enjoyed reading about my favorite cameras and drones of 2026 so far just as much as I've enjoyed writing about them. And like I said, do let me know in the comments what's been your favorite release this year!</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/tech/the-best-tech-of-2026-so-far-26-products-weve-actually-tested-rated-and-loved">The best tech of 2026 (so far): 26 products we've actually tested, rated and loved</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-cameras,review-2196.html">The best cameras in 2026</a></li></ul><div data-widget-type="multimodelreview" data-model-name="Kodak Charmera,DJI Osmo Pocket 4,DJI Osmo Pocket 4P,DJI Avata 360,Canon EOS R6 Mark III,Leica SL3-P" data-widget-title="Today's best camera and drone deals"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/my-favorite-cameras-drones-of-2026-so-far</link>
                                                                            <description>
                            <![CDATA[ So far in 2026, I've reviewed 12 cameras and drones, and most of them have been great. I've rounded up my 5 favorite launches from DJI, Leica, Canon and more. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">fu6oRbiFGucyM7BebmFdqB</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/dC5BFQtr2RkKfex6uJSyok-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Tue, 28 Jul 2026 09:30:00 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras & Photography]]></category>
                                                                                                <author><![CDATA[ nikita.achanta@futurenet.com (Nikita Achanta) ]]></author>                    <dc:creator><![CDATA[ Nikita Achanta ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/oXuvixDz99SbZp9z8Uoor3.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/dC5BFQtr2RkKfex6uJSyok-1280-80.jpg">
                                                            <media:credit><![CDATA[Nikita Achanta / Tom&#039;s Guide]]></media:credit>
                                                                                                                                                                                                                                    <media:description><![CDATA[A DJI Osmo Pocket 4P, Leica SL3-P, DJI Avata 360]]></media:description>                                                            <media:text><![CDATA[A DJI Osmo Pocket 4P, Leica SL3-P, DJI Avata 360]]></media:text>
                                <media:title type="plain"><![CDATA[A DJI Osmo Pocket 4P, Leica SL3-P, DJI Avata 360]]></media:title>
                                                    </media:content>
                                                    <media:thumbnail url="https://cdn.mos.cms.futurecdn.net/dC5BFQtr2RkKfex6uJSyok-1280-80.jpg" />
                                                                                                                                                                    <content:encoded >
                            <![CDATA[
                            <article>
                                <p>Every time the half-year mark comes around, I think to myself, "This has been a great year for cameras." It happened last year and the year before, and it has happened again this year. So far in 2026, we've been treated to some fantastic cameras and drones, and brands continue to prove that there's still room to grow and develop in terms of imaging tech.</p><p>If you don't know me, my name is Nikita and I'm a Senior Reviews Writer, testing the <a href="https://www.tomsguide.com/us/best-drones,review-2412.html">best drones</a> and the <a href="https://www.tomsguide.com/us/best-cameras,review-2196.html">best cameras</a>. This year, I've tested 12 of these nifty gadgets so far — from keychain cameras like the <a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review">Kodak Charmera</a> and black-and-white snappers like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-q3-monochrom-review">Leica Q3 Monochrom</a>, to speedy mirrorless beasts like the <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review">Canon EOS R6 Mark III</a> and aerial wonders like the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-avata-360-review">DJI Avata 360</a>.</p><p>After a somewhat slow start to the year, we've been spoiled thanks to new launches from the likes of Leica, and DJI coming out with a range of drones and gimbal cameras. Now that we're well into July, I thought I'd round up my top five cameras and drones of 2026 so far. Has there been a camera or drone that's been a standout for you? I'd love to hear from you in the comments!</p><h2 id="5-kodak-charmera">5. Kodak Charmera</h2><p><strong>Just a no-frills, fun keychain camera</strong><br><em>My rating: </em>★★★★☆</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:2048px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="6saMUxA9kPbPgFgeWW28PG" name="Kodak-Charmera-op-ed-7" alt="A photo of a Kodak Charmera keychain camera" src="https://cdn.mos.cms.futurecdn.net/6saMUxA9kPbPgFgeWW28PG.jpg" mos="" align="middle" fullscreen="" width="2048" height="1152" attribution="" class="inline"><img id="WqLzPy4iPS8qbMqK42dYLY" class="endorsement-img endorsement-bottom-right" style="max-width: 100px; max-height: 100px;" src="https://cdn.mos.cms.futurecdn.net/WqLzPy4iPS8qbMqK42dYLY.png" name="TG_badges_Recommended.png" alt="Tom's Guide Recommended product badge"></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 was putting this list together, I knew the Kodak Charmera was going to be included. If there's one thing 2026 has shown us already it's that cameras can be whimsical and just pure fun — and the Charmera proves it. You may have seen this nifty little keychain camera on TikTok or Instagram. It went viral earlier this year for its compact form factor and oh-so little asking price, and if you need a no-frills camera for a night out, this is the one to get.</p><figure role="gallery"><figure><img src="https://cdn.mos.cms.futurecdn.net/CVv9bGrVysbZQaXnVbH7e.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/7Cx73wSeWvkBVmo3gkkHe.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/eKe3jankij69jYdFfHeFf.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/kVABmfTeRPEeNn4oGBxji.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/WV7mFBFQGiyC7tAe9HwdmF.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/QJaxTurnLxgSWUhLQL5mfF.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/epp7DtZGJBrRdk66U4aJaF.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure><figure><img src="https://cdn.mos.cms.futurecdn.net/ZPjnnhLUJw337C4KfG6yaF.jpg" alt="A photo taken on a Kodak Charmera keychain camera" /><figcaption><small role="credit">Nikita Achanta / Tom's Guide</small></figcaption></figure></figure><p>Though the camera is fitted with a 1.6MP 1/4-inch CMOS sensor that doesn't take award-winning images, the Charmera is all about fun. The charm lies in its lo-fi images that remind me of the photos in my family's photo albums. What makes the Charmera unique is its extremely portable body — clip it on to your keys or your bag and you're good to go.</p><p>What I love most about the Charmera is its ease of use. Your 5-year-old niece who's just getting to grips with using her hands will be able to use it, as will your 60-year-old uncle who just wants to immortalize memories without caring much about image quality. Pass it around a table at a pub, or add it to the camera gear that's going to be at your wedding — the Charmera is for everyone.</p><p><em><strong>Read my full </strong></em><a href="https://www.tomsguide.com/cameras-photography/kodak-charmera-review"><em><strong>Kodak Charmera review</strong></em></a><em><strong>.</strong></em></p><div data-widget-type="review" data-model-name="Kodak Charmera"></div><h2 id="4-dji-osmo-pocket-4p">4. DJI Osmo Pocket 4P</h2><p><strong>DJI's best gimbal camera just got better</strong><br><em>My rating: Not scored yet</em></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width: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>The DJI Osmo Pocket 4P is perhaps one of the industry's worst kept secrets. Rumors have been circulating for months, so when the <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review">DJI Osmo Pocket 4</a> dropped, people wondered why it didn't have two dual lenses — and then the Osmo Pocket 4P dropped. Well, you can't buy it yet, but it's out there. I've held it; I've recorded footage on it in Malta; and I can tell you that it's very real. I can also tell you that this gimbal camera might give the Insta360 Luna Ultra a run for its money.</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/0RR3ZHn9PIs" allowfullscreen></iframe></div></div><p>But my lips are sealed due to embargoes and restrictions, and all I can really tell you about the Osmo Pocket 4P is this: it features D-Log2 support and a whopping 17 stops of dynamic range, and it comes in black and white. It's also got two lenses, one of which is a 60mm medium-telephoto.</p><p>I've spent a fair amount of time using the Osmo Pocket 4P, and it didn't take long for it to become one of my favorite cameras of 2026 so far. And why wouldn't it be? It builds on the success of the base Osmo Pocket 4 (which received a 5/5 rating from Senior Reviews Editor, Peter Wolinski). If you haven't bought the base model, I think it's worth waiting for the Osmo Pocket 4P. Trust me, I've been gobsmacked by it, and I'm sure you will be too.</p><p><em><strong>Read my </strong></em><a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4p-first-impressions"><em><strong>first impressions of the DJI Osmo Pocket 4P</strong></em></a><em><strong>.</strong></em></p><div data-widget-type="review" data-model-name="DJI Osmo Pocket 4"></div><h2 id="3-canon-eos-r6-mark-iii">3. Canon EOS R6 Mark III</h2><p><strong>Good becomes great</strong><br><em>My rating: </em>★★★★<strong>½</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1144px;"><p class="vanilla-image-block" style="padding-top:56.29%;"><img id="D7e7kySfUuavP3WUz4GNB7" name="EOSR6III" alt="A woman holding a Canon EOS R6 Mark III mirrorless camera" src="https://cdn.mos.cms.futurecdn.net/D7e7kySfUuavP3WUz4GNB7.jpg" mos="" align="middle" fullscreen="" width="1144" height="644" attribution="" class="inline"><img id="Xc987sPL6EDZGCrLfXakAb" class="endorsement-img endorsement-bottom-right" style="max-width: 100px; max-height: 100px;" src="https://cdn.mos.cms.futurecdn.net/Xc987sPL6EDZGCrLfXakAb.png" name="TG_ed_choice.png" alt="Editor's Choice"></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 succeeded the second-gen model in November 2025, but I tested the camera in 2026, so it counts! Good became great as Canon introduced substantial upgrades to its predecessor to deliver one of the best mid-range cameras. Having taken over 6,000 photos on the EOS R6 Mark III, I've been extremely impressed with its capabilities. The camera packs a 32.5MP full frame sensor that captures plenty of detail and gives you some room for making crops in post-production, too.</p><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/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/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/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/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/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/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><p>Combine that beautiful sensor with 40fps burst shooting and best-in-class autofocus, and you've got yourself a powerful camera for sports, wildlife, portraits, landscapes, you name it. As someone who loves photographing birds and animals, I appreciated the EOS R6 Mark III's ability to quickly lock on to subjects and capture stunning images. Canon's vaunted color science is the cherry on top of the cake.</p><p>And it's a delicious cake topped with many, many cherries. 7K Open Gate video and 4K/120fps video capabilities make the EOS R6 Mark III a competent hybrid camera. The addition of CFExpress support means the camera can accommodate 7K RAW files and sustain its rapid 40fps drive.</p><p><em><strong>Read my full </strong></em><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r6-mark-iii-review"><em><strong>Canon EOS R6 Mark III review</strong></em></a><em><strong>.</strong></em></p><div data-widget-type="review" data-model-name="Canon EOS R6 Mark III"></div><h2 id="2-leica-sl3-p">2. Leica SL3-P</h2><p><strong>Finally taking the fight to Canon and Sony</strong><br><em>My rating: </em>★★★★<strong>½</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width: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="" class="inline"><img id="Xc987sPL6EDZGCrLfXakAb" class="endorsement-img endorsement-top-left" style="max-width: 100px; max-height: 100px;" src="https://cdn.mos.cms.futurecdn.net/Xc987sPL6EDZGCrLfXakAb.png" name="TG_ed_choice.png" alt="Editor's Choice"></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 a legacy brand and though it has always made excellent (albeit <em>very </em>expensive) cameras, it’s always struggled to compete with heavy hitters like Sony and Canon, in my opinion — until now. The Leica SL3-P is one of the finest mirrorless cameras I’ve ever tested, and certainly the best of 2026 so far. What makes the SL3-P a standout is its sublime handling, superb build quality (you'd expect nothing less at this price), and imaging prowess.</p><figure role="gallery"><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/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/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/VkwACTGDWLmpQqERZbQWaW.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/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/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><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/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><p>I was seriously impressed with its 40fps high-speed drive and best-in-class autofocus that ensures you never miss an action shot — and as a wildlife photographer, I love it. Throw in a 44MP sensor, 8K Open Gate video, strong dynamic range performance, and sublime handling, and you’ve got yourself a winner. Well done, Leica.</p><p>The SL3-P makes Leica a serious alternative to Canon and Sony, and if you think, "Nope, Leicas are way too expensive," you'd be wrong. Sony's flagship <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a1-ii-review">A1 II </a>and Canon's <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r5-mark-ii-review">EOS R5 Mark II</a> bodies cost almost the same. I don't think Leica's price tag should scare you any longer — especially when you're getting a ridiculously sharp sensor.</p><p><em><strong>Read my full </strong></em><a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/leica-sl3-p-review"><em><strong>Leica SL3-P review.</strong></em></a></p><div data-widget-type="review" data-model-name="Leica SL3-P"></div><h2 id="1-dji-avata-360">1. DJI Avata 360</h2><p><strong>DJI claims its stake as the king of the skies</strong><br><em>My rating: </em>★★★★<strong>½</strong></p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1920px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="b4HLoC8e5MA5dACvMWYTcn" name="DJI_Avata_360" alt="A DJI Avata 360 360-degree drone" src="https://cdn.mos.cms.futurecdn.net/b4HLoC8e5MA5dACvMWYTcn.jpg" mos="" align="middle" fullscreen="" width="1920" height="1080" attribution="" class="inline"><img id="Xc987sPL6EDZGCrLfXakAb" class="endorsement-img endorsement-bottom-right" style="max-width: 100px; max-height: 100px;" src="https://cdn.mos.cms.futurecdn.net/Xc987sPL6EDZGCrLfXakAb.png" name="TG_ed_choice.png" alt="Editor's Choice"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Tom's Guide)</span></figcaption></figure><p>When the <a href="https://www.tomsguide.com/cameras-photography/drones/antigravity-a1-review">Antigravity A1</a>, the world's first 360° drone came out last year, I thought to myself, "This is an excellent drone; I wonder what DJI's answer is going to be." And DJI's answer was simple: the Avata 360 did everything the Antigravity A1 does but better, and at a much lower price. It undercut its primary rival, and DJI delivered a 360° drone that doesn't give you much to complain about.</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/DtyupaS6iLc" allowfullscreen></iframe></div></div><p>I absolutely adore the Avata 360, and as someone who has spent nearly a thousand hours flying, testing, writing and reading about drones, it's one of the best today, period. Fitted with twin lenses that capture every angle in 8K at 60fps without any stitching lines, and 120MP hi-res stills, the Avata 360 is a formidable content creation tool.</p><p>Exceptional build quality, dual ways to fly (FPV and with a standard controller), user-friendly software for editing, you name it: the Avata 360 does it all. Out of all the cameras and drones I've tested this year so far, this is the one I come back to time and again. DJI, despite <a href="https://www.tomsguide.com/cameras-photography/drones/us-drone-ban-update">facing a ban on its drones and enhanced scrutiny in the U.S.</a>, shows no signs of stopping with its Avata 360, Lito X1 and Lito 1 quadcopters, and I can't wait to see what else the Chinese giant has up its sleeve going forward.</p><p><em><strong>Read my full </strong></em><a href="https://www.tomsguide.com/cameras-photography/drones/dji-avata-360-review"><em><strong>DJI Avata 360 review</strong></em></a><em><strong>.</strong></em></p><div data-widget-type="review" data-model-name="DJI Avata 360"></div><h2 id="there-s-more-where-that-came-from">There's more where that came from...</h2><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="48Awcj5xnebuk8DUcES6QK" name="Instax-mini-evo-cinema-hands-on" alt="A black Fujifilm Instax mini Evo Cinema hybrid instant camera" src="https://cdn.mos.cms.futurecdn.net/48Awcj5xnebuk8DUcES6QK.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, of course, other cameras I've tested that haven't made the list, but they still deserve a shoutout. The first honorable mention that springs to my mind is the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-evo-cinema-review">Fujifilm Instax mini Evo Cinema</a>, a hybrid instant camera that redefines everything we know about instant cameras.</p><p>It's one of a kind: it lets you shoot photos and print directly from your smartphone, and you can record video clips up to 15 seconds long, choose a frame, and print on Instax film with a QR code that plays said video on a smartphone. It's fun and it's different, and it earned a 4.5-star rating from me.</p><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width: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>Then there's the <a href="https://www.tomsguide.com/cameras-photography/fujifilm-instax-mini-13-review">Fujifilm Instax mini 13</a>, which takes things back to basics. It's an analog instant cameras that simply shoots and prints photos within 90 seconds. Image and print quality is great, with retro colors that remind us all why we fell in love with instant photography in the first place.</p><p>I also tested the <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-3-review">Potensic Atom 3</a>, a powerful DJI alternative that, for the first time on a Potensic drone, introduces 4K/60fps video and RAW images. At an affordable price tag, the Atom 3 packs up to 50 minutes of flight time and accurate AI subject tracking.</p><p>But that's all from me (for now). I hope you've enjoyed reading about my favorite cameras and drones of 2026 so far just as much as I've enjoyed writing about them. And like I said, do let me know in the comments what's been your favorite release this year!</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/tech/the-best-tech-of-2026-so-far-26-products-weve-actually-tested-rated-and-loved">The best tech of 2026 (so far): 26 products we've actually tested, rated and loved</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-cameras,review-2196.html">The best cameras in 2026</a></li></ul><div data-widget-type="multimodelreview" data-model-name="Kodak Charmera,DJI Osmo Pocket 4,DJI Osmo Pocket 4P,DJI Avata 360,Canon EOS R6 Mark III,Leica SL3-P" data-widget-title="Today's best camera and drone deals"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ Perseid meteor shower 2026: The best cameras and telescopes to capture the action ]]></title>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & 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>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & 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>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & 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>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & 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>
                                                                                                <dc:content><![CDATA[ <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 </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> ]]></dc:content>
                                                                                                                                            <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 & 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 </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>
                                                                                                <dc:content><![CDATA[ <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="Luna Ultra" data-dimension48="Luna Ultra" 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="Luna Ultra" data-dimension48="Luna Ultra" 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="low" 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> ]]></dc:content>
                                                                                                                                            <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 & 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="Luna Ultra" data-dimension48="Luna Ultra" 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="Luna Ultra" data-dimension48="Luna Ultra" 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="low" 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>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & 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>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & 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>
                                                                                                <dc:content><![CDATA[ <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="Charmera" data-dimension48="Charmera" 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="Charmera" data-dimension48="Charmera" 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="Go 3S (128 GB)" data-dimension48="Go 3S (128 GB)" 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><strong><a 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="Go 3S (128 GB)" data-dimension48="Go 3S (128 GB)" data-dimension25="$524.99">Go 3S (128 GB): was $769.99 now $524.99</a></strong><br>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="Go 3S (128 GB)" data-dimension48="Go 3S (128 GB)" 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="Instax SQ1" data-dimension48="Instax SQ1" 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="Instax SQ1" data-dimension48="Instax SQ1" 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="Go Gen 2" data-dimension48="Go Gen 2" 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="Go Gen 2" data-dimension48="Go Gen 2" 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="Go Gen 2" data-dimension48="Go Gen 2" 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="Pixpro C1 Digital Camera" data-dimension48="Pixpro C1 Digital Camera" 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><strong><a 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="Pixpro C1 Digital Camera" data-dimension48="Pixpro C1 Digital Camera" data-dimension25="$147.36">Pixpro C1 Digital Camera: was $200 now $147.36</a></strong><br>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="Pixpro C1 Digital Camera" data-dimension48="Pixpro C1 Digital Camera" 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="Snap" data-dimension48="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="Snap" data-dimension48="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="Snap" data-dimension48="Snap" data-dimension25="$89">View Deal</a></p></div><div data-widget-type="multimodelreview" data-model-name="FujiFilm Instax Mini 9,Kodak Charmera,Camp Snap" data-widget-title="Today's best cheap camera deals"></div> ]]></dc:content>
                                                                                                                                            <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 & 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="Charmera" data-dimension48="Charmera" 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="Charmera" data-dimension48="Charmera" 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="Go 3S (128 GB)" data-dimension48="Go 3S (128 GB)" 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><strong><a 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="Go 3S (128 GB)" data-dimension48="Go 3S (128 GB)" data-dimension25="$524.99">Go 3S (128 GB): was $769.99 now $524.99</a></strong><br>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="Go 3S (128 GB)" data-dimension48="Go 3S (128 GB)" 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="Instax SQ1" data-dimension48="Instax SQ1" 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="Instax SQ1" data-dimension48="Instax SQ1" 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="Go Gen 2" data-dimension48="Go Gen 2" 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="Go Gen 2" data-dimension48="Go Gen 2" 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="Go Gen 2" data-dimension48="Go Gen 2" 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="Pixpro C1 Digital Camera" data-dimension48="Pixpro C1 Digital Camera" 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><strong><a 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="Pixpro C1 Digital Camera" data-dimension48="Pixpro C1 Digital Camera" data-dimension25="$147.36">Pixpro C1 Digital Camera: was $200 now $147.36</a></strong><br>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="Pixpro C1 Digital Camera" data-dimension48="Pixpro C1 Digital Camera" 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="Snap" data-dimension48="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="Snap" data-dimension48="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="Snap" data-dimension48="Snap" data-dimension25="$89">View Deal</a></p></div><div data-widget-type="multimodelreview" data-model-name="FujiFilm Instax Mini 9,Kodak Charmera,Camp Snap" data-widget-title="Today's best cheap camera deals"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
                                <item>
                                                            <title><![CDATA[ HOVERAir AQUA review: This drone is fearless over water ]]></title>
                                                                                                <dc:content><![CDATA[ <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 </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> ]]></dc:content>
                                                                                                                                            <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 & 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 </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>
                                                                                                <dc:content><![CDATA[ <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="Atom 3" data-dimension48="Atom 3" 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="Atom 3" data-dimension48="Atom 3" 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="Mini 5 Pro" data-dimension48="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="Mini 5 Pro" data-dimension48="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="Mini 5 Pro" data-dimension48="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="Neo 2" data-dimension48="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="Neo 2" data-dimension48="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="Neo 2" data-dimension48="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="Flip" data-dimension48="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="Flip" data-dimension48="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="Flip" data-dimension48="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="Lito X1" data-dimension48="Lito X1" 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="Lito X1" data-dimension48="Lito X1" 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="Lito 1" data-dimension48="Lito 1" 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="Lito 1" data-dimension48="Lito 1" 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> ]]></dc:content>
                                                                                                                                            <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 & 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="Atom 3" data-dimension48="Atom 3" 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="Atom 3" data-dimension48="Atom 3" 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="Mini 5 Pro" data-dimension48="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="Mini 5 Pro" data-dimension48="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="Mini 5 Pro" data-dimension48="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="Neo 2" data-dimension48="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="Neo 2" data-dimension48="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="Neo 2" data-dimension48="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="Flip" data-dimension48="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="Flip" data-dimension48="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="Flip" data-dimension48="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="Lito X1" data-dimension48="Lito X1" 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="Lito X1" data-dimension48="Lito X1" 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="Lito 1" data-dimension48="Lito 1" 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="Lito 1" data-dimension48="Lito 1" 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>
                                                                                                <dc:content><![CDATA[ <p>As we get ever closer to the 4th of July, the big sales keep on coming. Yesterday I covered the best <a href="https://www.tomsguide.com/tech/18-best-target-4th-of-july-tech-deals-save-big-on-headphones-laptops-and-wearables?hasComeFromProof=true" target="_blank">Target tech deals</a> and now I'm looking at the retailer's enviable selection of cameras.</p><p>If you're looking to step up from taking snaps on your smartphone, then you'll want to at least consider a DSLR. I've picked out some top models from Canon, Nikon and Sony here, across a range of different price points.</p><p>Alternatively, you might be after something much simpler. If so, I've also selected a few cool instant cameras that are just the thing for taking on your summer trips.  </p><p>There are lots more things to buy in the <a href="https://www.tomsguide.com/sales-events/best-4th-of-july-sales-2026-walmart-home-depot-more">4th of July sales</a>. If you're looking for more deals then check out our guides to <a href="https://www.tomsguide.com/home/home-appliances/best-buy-4th-of-july-sale-huge-appliance-discounts-from-ge-lg-and-samsung-save-up-to-usd1560" target="_blank">Best Buy's appliance sale</a>, as well as its <a href="https://www.tomsguide.com/tvs/4th-of-july-tv-deals-save-up-to-usd1-700-on-lg-samsung-and-more-at-best-buy" target="_blank">TV</a> and <a href="https://www.tomsguide.com/audio/bluetooth-speakers/best-buy-4th-of-july-sale-score-up-to-33-percent-off-these-9-bluetooth-speakers-from-sony-klipsch-jbl-and-more" target="_blank">audio sales </a>too.</p>        <div class="featured_product_block featured_block_hero" data-id="7a28519a-b1b6-4e03-bbb0-24d7e774332d">            <a href="https://www.target.com/p/kodak-smile-classic-digital-instant-camera-for-3-5-x-4-25-zink-photo-paper-bluetooth-16mp-pictures/-/A-82282261?preselect=82282260#lnk=sametab" data-model-name="Kodak Smile Classic Digital Instant Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/AC8Vs4gfNxRzcMWKee7958.webp" alt="Kodak Smile Classic Digital Instant Camera for 3.5 X 4.25 Zink Photo Paper - Bluetooth, 16mp Pictures (red)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Kodak</div>                                        <div class="featured__title">Kodak Smile Classic Digital Instant Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="57ca64da-74eb-421e-876a-72633ae005f7">            <a href="https://www.target.com/p/kodak-pixpro-fz55-digital-camera-red-32gb-memory-card-camera-case/-/A-1006353493#lnk=sametab" data-model-name="Kodak PIXPRO FZ55 Camera with 32GB Memory Card & Camera Case" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/GHmzZL8oME8Zn75RW7dEH4.webp" alt="Kodak Pixpro Fz55 Digital Camera Red With 32gb Memory Card & Camera Case"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Kodak</div>                                        <div class="featured__title">Kodak PIXPRO FZ55 Camera with 32GB Memory Card & Camera Case</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="f6205ebf-d2cc-4a4b-91b4-c76c63f8699c">            <a href="https://www.target.com/p/fujifilm-instax-mini-12-camera-blossom-pink-case-instax-mini-instant-film-100-sheets-mini-photo-album/-/A-1007330499#lnk=sametab" data-model-name="Fujifilm Instax Mini 12 Camera with Instant Film and Photo Album" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/umzweWZ2AXFxdD9cUVwnd7.webp" alt="Fujifilm Instax Mini 12 Camera (blossom Pink)   Case   Instax Mini Instant Film (100 Sheets)   Mini Photo Album"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Fujifilm</div>                                        <div class="featured__title">Fujifilm Instax Mini 12 Camera with Instant Film and Photo Album</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="12681ba5-42de-4eef-8233-f0a0573613d4">            <a href="https://www.target.com/p/canon-eos-r100-rf-s18-45mm-f4-5-6-3-is-stm-lens-kit/-/A-89177813#lnk=sametab" data-model-name="Canon EOS R100 Mirrorless Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/kJtFV92p9aVnXbRg7gFNdR.webp" alt="Canon Eos R100 Rf-S18-45mm F4.5-6.3 Is Stm Lens Kit: 24.1mp Mirrorless, Wi-Fi"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Canon EOS R100 Mirrorless Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="d29abe2e-8f7f-4c0b-a05d-1be82ae1bdf2">            <a href="https://www.target.com/p/om-system-om-d-e-m10-mark-iv-black-mirrorless-camera-with-14-42mm-ez-lens-kit/-/A-1000985462#lnk=sametab" data-model-name="OM System OM-D E-M10 Mark IV Mirrorless Camera with 14-42mm EZ Lens Kit" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/ebsNY4SqyPTBU7fJtrgQg9.webp" alt="Om System Om-D E-M10 Mark Iv Black Mirrorless Camera With 14-42mm Ez Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>OM System</div>                                        <div class="featured__title">OM System OM-D E-M10 Mark IV Mirrorless Camera with 14-42mm EZ Lens Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="0bc625ae-d059-469e-8714-95d2da4ffe7f">            <a href="https://www.target.com/p/canon-eos-r50-rf-s18-45mm-f4-5-6-3mm-is-stm-kit/-/A-88661030#lnk=sametab" data-model-name="Canon EOS R50 Mirrorless Digital Camera" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/UFvqDWznhiVZNcWmoUNBe6.webp" alt="Canon Eos R50 Rf-S18-45mm F4.5-6.3mm Is Stm Kit: Mirrorless Wi-Fi Digital Camera"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Canon</div>                                        <div class="featured__title">Canon EOS R50 Mirrorless Digital Camera</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="3e94430b-90b1-400d-9a7b-1beb8d3a9c35">            <a href="https://www.target.com/p/sony-alpha-zv-e10-mirrorless-vlog-camera-with-16-50mm-f-3-5-5-6-ii-lens-black/-/A-1009791818#lnk=sametab" data-model-name="Sony Alpha ZV-E10 Mirrorless Camera with 16-50mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/jbohhK55NQvx38erhntf9S.webp" alt="Sony Alpha Zv-E10 Mirrorless Vlog Camera With 16-50mm F/3.5-5.6 Ii Lens (black)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Sony</div>                                        <div class="featured__title">Sony Alpha ZV-E10 Mirrorless Camera with 16-50mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="fb05059f-7f53-4390-9a9d-4917736042c8">            <a href="https://www.target.com/p/canon-eos-rp-mirrorless-digital-camera-with-24-105mm-f-4-7-1-stm-lens-kit/-/A-83999286#lnk=sametab" data-model-name="Canon EOS RP Mirrorless Digital Camera with 24-105mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/PRYXSrS7ZXnrSfAWLSCmxW.webp" alt="Canon Eos Rp Mirrorless Digital Camera With 24-105mm F/4-7.1 Stm Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Canon</div>                                        <div class="featured__title">Canon EOS RP Mirrorless Digital Camera with 24-105mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="1c170288-f19c-4b5b-9d91-191f36bc6c0a">            <a href="https://www.target.com/p/om-system-om-5-mark-ii-black-mirrorless-camera-with-14-150mm-lens-kit/-/A-1006521726#lnk=sametab" data-model-name="Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/rH274JEp4qE3KVmz4ziDx8.webp" alt="Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all OM System</div>                                        <div class="featured__title">Om System Om-5 Mark Ii Black Mirrorless Camera With 14-150mm Lens Kit</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="9b14d8be-01ff-4767-93c7-74d9b58ce0ff">            <a href="https://www.target.com/p/sony-alpha-zve10-ii-aps-c-interchangeable-lens-mirrorless-designed-for-content-creators-body-only-black/-/A-84244533#lnk=sametab" data-model-name="Sony Alpha A7 III Mirrorless Camera with 28-70mm Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/tyKydjCtF6TBXUuSpLiQVS.webp" alt="Sony Alpha A7 Iii 24.2mp Full Frame Mirrorless Digital Camera With 28-70mm Lens"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Sony</div>                                        <div class="featured__title">Sony Alpha A7 III Mirrorless Camera with 28-70mm Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="e1779bc6-9c01-4a08-bfea-d89cf9a20d98">            <a href="https://www.target.com/p/nikon-z5-ii-mirrorless-digital-camera-with-z-24-200mm-f-4-6-3-vr-lens/-/A-1003487515#lnk=sametab" data-model-name="Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/Nahbx7MHmvTqeuhDcVk9s8.webp" alt="Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Shop all Nikon</div>                                        <div class="featured__title">Nikon Z5 Ii Mirrorless Digital Camera With Z 24 200mm F 4 6.3 Vr Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div>        <div class="featured_product_block featured_block_hero" data-id="a0e6535a-2b3d-40ee-8226-29b8f030c5f1">            <a href="https://www.target.com/p/nikon-zf-mirrorless-camera-with-24-70mm-f-4-lens-24-5mp-4k-uhd-video-silver/-/A-1007130727#lnk=sametab" data-model-name="Nikon Zf Mirrorless Camera with 24-70mm F/4 Lens" data-model-brand="" ><div class='product-image-widthsetter'><p class='vanilla-image-block' data-bordeaux-image-check style='padding-top:100.00%';><img style="width: 100%" class="featured_image" src="https://cdn.mos.cms.futurecdn.net/GLn79FhaFACLS65qjRv877.webp" alt="Nikon Zf Mirrorless Camera With 24-70mm F/4 Lens - 24.5mp, 4k Uhd Video (silver)"></p></div></a>            <div class="featured_product_details_wrapper">                <div class="featured_product_title_wrapper">                                        <div class='featured__brand'>Nikon</div>                                        <div class="featured__title">Nikon Zf Mirrorless Camera with 24-70mm F/4 Lens</div>                                    </div>                <div class="subtitle__description">                                                            <p></p>                </div>                            </div>        </div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/targets-massive-4th-of-july-sale-cuts-up-to-usd600-off-cameras-big-savings-on-canon-nikon-and-more</link>
                                                                            <description>
                            <![CDATA[ Target's 4th of July sale has slashed prices on a range of cameras, from feature-packed DSLR models to cool instant snappers. Check out my 12 favorite bargains. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">3bYZwuJHUKiLheiGp8BLKd</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/gyY9fdEWMLHEigw3b4g8L7-1280-80.jpg" type="image/jpeg" length="0"></enclosure>
                                                                        <pubDate>Fri, 03 Jul 2026 16:26:27 +0000</pubDate>                                                                                                                                                                                                                                <category><![CDATA[Cameras & Photography]]></category>
                                                                                                                    <dc:creator><![CDATA[ Will Salmon ]]></dc:creator>                                                                                    <dc:source><![CDATA[ https://cdn.mos.cms.futurecdn.net/HpRWP7Mh9ErgRGoTvZBbBJ.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>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & 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>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & 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>
                                                                                                <dc:content><![CDATA[ <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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Luna Ultra" data-dimension48="Luna Ultra" 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="Luna Ultra" data-dimension48="Luna Ultra" 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="Osmo Pocket 4" data-dimension48="Osmo Pocket 4" 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="Osmo Pocket 4" data-dimension48="Osmo Pocket 4" 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> ]]></dc:content>
                                                                                                                                            <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 & 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Luna Ultra" data-dimension48="Luna Ultra" 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="Luna Ultra" data-dimension48="Luna Ultra" 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="Osmo Pocket 4" data-dimension48="Osmo Pocket 4" 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="Osmo Pocket 4" data-dimension48="Osmo Pocket 4" 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>
                                                                                                <dc:content><![CDATA[ <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="Mini 3" data-dimension48="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="Mini 3" data-dimension48="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="Mini 3" data-dimension48="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="Flip" data-dimension48="Flip" 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="Flip" data-dimension48="Flip" 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="Osmo Action 6 Capture More Combo" data-dimension48="Osmo Action 6 Capture More Combo" 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="Osmo Action 6 Capture More Combo" data-dimension48="Osmo Action 6 Capture More Combo" 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="Osmo Action 6 Capture More Combo" data-dimension48="Osmo Action 6 Capture More Combo" 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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" 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><strong><a 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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" data-dimension25="$512">Osmo Pocket 3 Creator Combo: was $629 now $512</a></strong><br>The <a href="https://www.tomsguide.com/reviews/dji-osmo-pocket-3">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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" 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> ]]></dc:content>
                                                                                                                                            <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 & 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="Mini 3" data-dimension48="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="Mini 3" data-dimension48="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="Mini 3" data-dimension48="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="Flip" data-dimension48="Flip" 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="Flip" data-dimension48="Flip" 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="Osmo Action 6 Capture More Combo" data-dimension48="Osmo Action 6 Capture More Combo" 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="Osmo Action 6 Capture More Combo" data-dimension48="Osmo Action 6 Capture More Combo" 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="Osmo Action 6 Capture More Combo" data-dimension48="Osmo Action 6 Capture More Combo" 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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" 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><strong><a 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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" data-dimension25="$512">Osmo Pocket 3 Creator Combo: was $629 now $512</a></strong><br>The <a href="https://www.tomsguide.com/reviews/dji-osmo-pocket-3">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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" 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>
                                                                                                <dc:content><![CDATA[ <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" data-dimension48="EOS R50 V" 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" data-dimension48="EOS R50 V" 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" data-dimension48="EOS R50 V" 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="A7 III" data-dimension48="A7 III" 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="A7 III" data-dimension48="A7 III" 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="S1RII" data-dimension48="S1RII" 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="S1RII" data-dimension48="S1RII" 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="S1RII" data-dimension48="S1RII" 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="Osmo Action 5 Pro" data-dimension48="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="Osmo Action 5 Pro" data-dimension48="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="Osmo Action 5 Pro" data-dimension48="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="Osmo Pocket 3 Drive bundle" data-dimension48="Osmo Pocket 3 Drive bundle" 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><strong><a 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="Osmo Pocket 3 Drive bundle" data-dimension48="Osmo Pocket 3 Drive bundle" data-dimension25="$375">Osmo Pocket 3 Drive bundle: was $496.99 now $375</a></strong><br>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="Osmo Pocket 3 Drive bundle" data-dimension48="Osmo Pocket 3 Drive bundle" 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="mini 12" data-dimension48="mini 12" 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="mini 12" data-dimension48="mini 12" 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> ]]></dc:content>
                                                                                                                                            <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 & 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" data-dimension48="EOS R50 V" 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" data-dimension48="EOS R50 V" 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" data-dimension48="EOS R50 V" 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="A7 III" data-dimension48="A7 III" 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="A7 III" data-dimension48="A7 III" 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="S1RII" data-dimension48="S1RII" 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="S1RII" data-dimension48="S1RII" 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="S1RII" data-dimension48="S1RII" 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="Osmo Action 5 Pro" data-dimension48="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="Osmo Action 5 Pro" data-dimension48="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="Osmo Action 5 Pro" data-dimension48="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="Osmo Pocket 3 Drive bundle" data-dimension48="Osmo Pocket 3 Drive bundle" 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><strong><a 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="Osmo Pocket 3 Drive bundle" data-dimension48="Osmo Pocket 3 Drive bundle" data-dimension25="$375">Osmo Pocket 3 Drive bundle: was $496.99 now $375</a></strong><br>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="Osmo Pocket 3 Drive bundle" data-dimension48="Osmo Pocket 3 Drive bundle" 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="mini 12" data-dimension48="mini 12" 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="mini 12" data-dimension48="mini 12" 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>
                                                                                                <dc:content><![CDATA[ <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-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="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-3">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-3">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-2">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-2">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-2">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> ]]></dc:content>
                                                                                                                                            <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 & 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-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="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-3">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-3">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-2">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-2">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-2">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>
                                                                                                <dc:content><![CDATA[ <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="P4" data-dimension48="P4" 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><strong><a 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="P4" data-dimension48="P4" data-dimension25="$299">P4: was $499 now $299</a></strong><br>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"><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="P4" data-dimension48="P4" 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> ]]></dc:content>
                                                                                                                                            <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 & 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="P4" data-dimension48="P4" 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><strong><a 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="P4" data-dimension48="P4" data-dimension25="$299">P4: was $499 now $299</a></strong><br>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"><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="P4" data-dimension48="P4" 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>
                                                                                                <dc:content><![CDATA[ <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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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><strong><a 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" data-dimension25="$378">Osmo Pocket 3: was $499 now $378</a></strong><br>Unfortunately, the latest <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review" target="_blank">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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Osmo Action 5 Pro" data-dimension48="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="Osmo Action 5 Pro" data-dimension48="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="Osmo Action 5 Pro" data-dimension48="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="Osmo 360" data-dimension48="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="Osmo 360" data-dimension48="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="Osmo 360" data-dimension48="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="LIT Hero" data-dimension48="LIT Hero" 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="LIT Hero" data-dimension48="LIT Hero" 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="X4 Air" data-dimension48="X4 Air" 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="X4 Air" data-dimension48="X4 Air" 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="X3" data-dimension48="X3" 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="X3" data-dimension48="X3" 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Osmo Action 5 Pro" data-dimension48="Osmo Action 5 Pro" 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="Osmo Action 5 Pro" data-dimension48="Osmo Action 5 Pro" 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="Osmo Action 4" data-dimension48="Osmo Action 4" 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="Osmo Action 4" data-dimension48="Osmo Action 4" 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="Osmo Nano" data-dimension48="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="Osmo Nano" data-dimension48="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="Osmo Nano" data-dimension48="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> ]]></dc:content>
                                                                                                                                            <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 & Action Cameras]]></category>
                                                    <category><![CDATA[Cameras & 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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><strong><a 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" data-dimension25="$378">Osmo Pocket 3: was $499 now $378</a></strong><br>Unfortunately, the latest <a href="https://www.tomsguide.com/cameras-photography/dji-osmo-pocket-4-review" target="_blank">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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Osmo Action 5 Pro" data-dimension48="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="Osmo Action 5 Pro" data-dimension48="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="Osmo Action 5 Pro" data-dimension48="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="Osmo 360" data-dimension48="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="Osmo 360" data-dimension48="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="Osmo 360" data-dimension48="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="LIT Hero" data-dimension48="LIT Hero" 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="LIT Hero" data-dimension48="LIT Hero" 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="X4 Air" data-dimension48="X4 Air" 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="X4 Air" data-dimension48="X4 Air" 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="X3" data-dimension48="X3" 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="X3" data-dimension48="X3" 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Osmo Pocket 3" data-dimension48="Osmo Pocket 3" 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="Osmo Action 5 Pro" data-dimension48="Osmo Action 5 Pro" 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="Osmo Action 5 Pro" data-dimension48="Osmo Action 5 Pro" 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="Osmo Action 4" data-dimension48="Osmo Action 4" 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="Osmo Action 4" data-dimension48="Osmo Action 4" 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="Osmo Nano" data-dimension48="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="Osmo Nano" data-dimension48="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="Osmo Nano" data-dimension48="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>
                                                                                                <dc:content><![CDATA[ <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="Mini 3 Fly More Combo" data-dimension48="Mini 3 Fly More Combo" 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><strong><a 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="Mini 3 Fly More Combo" data-dimension48="Mini 3 Fly More Combo" data-dimension25="$449">Mini 3 Fly More Combo: was $719 now $449</a></strong><br>Though the <a href="https://www.tomsguide.com/reviews/dji-mini-3">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="Mini 3 Fly More Combo" data-dimension48="Mini 3 Fly More Combo" 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="Mini 4K Fly More Combo" data-dimension48="Mini 4K Fly More Combo" 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><strong><a 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="Mini 4K Fly More Combo" data-dimension48="Mini 4K Fly More Combo" data-dimension25="$255">Mini 4K Fly More Combo: was $389 now $255</a></strong><br>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="Mini 4K Fly More Combo" data-dimension48="Mini 4K Fly More Combo" 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="Neo" data-dimension48="Neo" 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="Neo" data-dimension48="Neo" 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="Neo" data-dimension48="Neo" 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="Flip (with two batteries)" data-dimension48="Flip (with two batteries)" 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><strong><a 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="Flip (with two batteries)" data-dimension48="Flip (with two batteries)" data-dimension25="$309">Flip (with two batteries): was $389 now $309</a></strong><br>In my <a href="https://www.tomsguide.com/cameras-photography/drones/dji-flip-review">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="Flip (with two batteries)" data-dimension48="Flip (with two batteries)" 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="A1 (Infinity Bundle)" data-dimension48="A1 (Infinity Bundle)" 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><strong><a 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="A1 (Infinity Bundle)" data-dimension48="A1 (Infinity Bundle)" data-dimension25="$1599">A1 (Infinity Bundle): was $1999 now $1599</a></strong><br>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="A1 (Infinity Bundle)" data-dimension48="A1 (Infinity Bundle)" 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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" 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><strong><a 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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" data-dimension25="$324">Atom 2 Fly More Combo: was $479 now $324</a></strong><br>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">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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" 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="Atom SE Expansion Kit" data-dimension48="Atom SE Expansion Kit" 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="Atom SE Expansion Kit" data-dimension48="Atom SE Expansion Kit" 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="X1 Combo Plus" data-dimension48="X1 Combo Plus" 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><strong><a 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="X1 Combo Plus" data-dimension48="X1 Combo Plus" data-dimension25="$271">X1 Combo Plus: was $399 now $271</a></strong><br>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">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="X1 Combo Plus" data-dimension48="X1 Combo Plus" 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="X1 ProMax" data-dimension48="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="X1 ProMax" data-dimension48="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="X1 ProMax" data-dimension48="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> ]]></dc:content>
                                                                                                                                            <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 & 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="Mini 3 Fly More Combo" data-dimension48="Mini 3 Fly More Combo" 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><strong><a 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="Mini 3 Fly More Combo" data-dimension48="Mini 3 Fly More Combo" data-dimension25="$449">Mini 3 Fly More Combo: was $719 now $449</a></strong><br>Though the <a href="https://www.tomsguide.com/reviews/dji-mini-3">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="Mini 3 Fly More Combo" data-dimension48="Mini 3 Fly More Combo" 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="Mini 4K Fly More Combo" data-dimension48="Mini 4K Fly More Combo" 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><strong><a 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="Mini 4K Fly More Combo" data-dimension48="Mini 4K Fly More Combo" data-dimension25="$255">Mini 4K Fly More Combo: was $389 now $255</a></strong><br>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="Mini 4K Fly More Combo" data-dimension48="Mini 4K Fly More Combo" 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="Neo" data-dimension48="Neo" 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="Neo" data-dimension48="Neo" 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="Neo" data-dimension48="Neo" 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="Flip (with two batteries)" data-dimension48="Flip (with two batteries)" 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><strong><a 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="Flip (with two batteries)" data-dimension48="Flip (with two batteries)" data-dimension25="$309">Flip (with two batteries): was $389 now $309</a></strong><br>In my <a href="https://www.tomsguide.com/cameras-photography/drones/dji-flip-review">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="Flip (with two batteries)" data-dimension48="Flip (with two batteries)" 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="A1 (Infinity Bundle)" data-dimension48="A1 (Infinity Bundle)" 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><strong><a 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="A1 (Infinity Bundle)" data-dimension48="A1 (Infinity Bundle)" data-dimension25="$1599">A1 (Infinity Bundle): was $1999 now $1599</a></strong><br>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="A1 (Infinity Bundle)" data-dimension48="A1 (Infinity Bundle)" 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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" 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><strong><a 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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" data-dimension25="$324">Atom 2 Fly More Combo: was $479 now $324</a></strong><br>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">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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" 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="Atom SE Expansion Kit" data-dimension48="Atom SE Expansion Kit" 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="Atom SE Expansion Kit" data-dimension48="Atom SE Expansion Kit" 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="X1 Combo Plus" data-dimension48="X1 Combo Plus" 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><strong><a 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="X1 Combo Plus" data-dimension48="X1 Combo Plus" data-dimension25="$271">X1 Combo Plus: was $399 now $271</a></strong><br>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">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="X1 Combo Plus" data-dimension48="X1 Combo Plus" 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="X1 ProMax" data-dimension48="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="X1 ProMax" data-dimension48="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="X1 ProMax" data-dimension48="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>
                                                                                                <dc:content><![CDATA[ <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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" 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><strong><a 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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" data-dimension25="$539">Osmo Pocket 3 Creator Combo: was $629 now $539</a></strong><br>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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" 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><div data-widget-type="multimodelreview" data-model-name="DJI Osmo Pocket 3 Creator Combo,DJI OSMO Pocket 3"></div> ]]></dc:content>
                                                                                                                                            <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 & 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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" 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><strong><a 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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" data-dimension25="$539">Osmo Pocket 3 Creator Combo: was $629 now $539</a></strong><br>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="Osmo Pocket 3 Creator Combo" data-dimension48="Osmo Pocket 3 Creator Combo" 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><div data-widget-type="multimodelreview" data-model-name="DJI Osmo Pocket 3 Creator Combo,DJI OSMO Pocket 3"></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>
                                                                                                <dc:content><![CDATA[ <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="EOS R6 Mark III" data-dimension48="EOS R6 Mark III" 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="EOS R6 Mark III" data-dimension48="EOS R6 Mark III" 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><div data-widget-type="review" data-model-name="Canon EOS R6 Mark III"></div> ]]></dc:content>
                                                                                                                                            <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 & 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="EOS R6 Mark III" data-dimension48="EOS R6 Mark III" 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="EOS R6 Mark III" data-dimension48="EOS R6 Mark III" 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><div data-widget-type="review" data-model-name="Canon EOS R6 Mark III"></div>
                                                            </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>
                                                                                                <dc:content><![CDATA[ <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-3">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-3">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-4">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-4">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-3">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-3">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-3">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> ]]></dc:content>
                                                                                                                                            <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 & 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-3">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-3">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-4">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-4">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-3">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-3">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-3">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>
                                                                                                <dc:content><![CDATA[ <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><div data-widget-type="simple" data-model-name="Insta360 Luna Ultra"></div> ]]></dc:content>
                                                                                                                                            <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 & 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><div data-widget-type="simple" data-model-name="Insta360 Luna Ultra"></div>
                                                            </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>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & Action Cameras]]></category>
                                                    <category><![CDATA[Cameras & 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>
                                                                                                <dc:content><![CDATA[ <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-5">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-4">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-5">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> ]]></dc:content>
                                                                                                                                            <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 & 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-5">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-4">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-5">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>
                                                                                                <dc:content><![CDATA[ <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><div data-widget-type="review" data-model-name="Apple iPhone 17 Pro"></div><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><div data-widget-type="review" data-model-name="Olympus OM-D E-M10 Mark IV,Olympus M.Zuiko Digital ED 12-200mm F3.5-6.3"></div><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> ]]></dc:content>
                                                                                                                                            <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 & 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><div data-widget-type="review" data-model-name="Apple iPhone 17 Pro"></div><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><div data-widget-type="review" data-model-name="Olympus OM-D E-M10 Mark IV,Olympus M.Zuiko Digital ED 12-200mm F3.5-6.3"></div><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>
                                                                                                <dc:content><![CDATA[ <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" data-dimension48="DJI Osmo Pocket 4" 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" data-dimension48="DJI Osmo Pocket 4" 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" data-dimension48="DJI Osmo Pocket 4" 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="Creator Combo" data-dimension48="Creator Combo" 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="Creator Combo" data-dimension48="Creator Combo" 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="Creator Combo" data-dimension48="Creator Combo" 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> ]]></dc:content>
                                                                                                                                            <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 & Action Cameras]]></category>
                                                    <category><![CDATA[Cameras & 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" data-dimension48="DJI Osmo Pocket 4" 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" data-dimension48="DJI Osmo Pocket 4" 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" data-dimension48="DJI Osmo Pocket 4" 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="Creator Combo" data-dimension48="Creator Combo" 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="Creator Combo" data-dimension48="Creator Combo" 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="Creator Combo" data-dimension48="Creator Combo" 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>
                                                                                                <dc:content><![CDATA[ <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="S30 Pro" data-dimension48="S30 Pro" 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="S30 Pro" data-dimension48="S30 Pro" 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="S30 Pro" data-dimension48="S30 Pro" 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> ]]></dc:content>
                                                                                                                                            <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 & 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="S30 Pro" data-dimension48="S30 Pro" 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="S30 Pro" data-dimension48="S30 Pro" 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="S30 Pro" data-dimension48="S30 Pro" 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>
                                                                                                <dc:content><![CDATA[ <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="GO 3S" data-dimension48="GO 3S" 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="GO 3S" data-dimension48="GO 3S" 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="Pet Harness" data-dimension48="Pet Harness" 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="Pet Harness" data-dimension48="Pet Harness" 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> ]]></dc:content>
                                                                                                                                            <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 & Action Cameras]]></category>
                                                    <category><![CDATA[Cameras & 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="GO 3S" data-dimension48="GO 3S" 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="GO 3S" data-dimension48="GO 3S" 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="Pet Harness" data-dimension48="Pet Harness" 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="Pet Harness" data-dimension48="Pet Harness" 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>
                                                                                                <dc:content><![CDATA[ <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-2">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-2">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-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="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-2">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-2">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> ]]></dc:content>
                                                                                                                                            <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 & Action Cameras]]></category>
                                                    <category><![CDATA[Cameras & 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-2">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-2">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-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="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-2">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-2">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>
                                                                                                <dc:content><![CDATA[ <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> ]]></dc:content>
                                                                                                                                            <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 & Action Cameras]]></category>
                                                    <category><![CDATA[iPhones]]></category>
                                                    <category><![CDATA[Cameras & 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>
                                                                                                <dc:content><![CDATA[ <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="X half" data-dimension48="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="X half" data-dimension48="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="X half" data-dimension48="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="PIXPRO FZ45" data-dimension48="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="PIXPRO FZ45" data-dimension48="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="PIXPRO FZ45" data-dimension48="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="PIXPRO FZ55" data-dimension48="PIXPRO FZ55" 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="PIXPRO FZ55" data-dimension48="PIXPRO FZ55" 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="PIXPRO C1" data-dimension48="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="PIXPRO C1" data-dimension48="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="PIXPRO C1" data-dimension48="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="Ektar H35N Bundle" data-dimension48="Ektar H35N Bundle" 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><strong><a 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="Ektar H35N Bundle" data-dimension48="Ektar H35N Bundle" data-dimension25="$79">Ektar H35N Bundle: was $89 now $79</a></strong><br>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="Ektar H35N Bundle" data-dimension48="Ektar H35N Bundle" 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> ]]></dc:content>
                                                                                                                                            <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 & 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="X half" data-dimension48="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="X half" data-dimension48="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="X half" data-dimension48="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="PIXPRO FZ45" data-dimension48="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="PIXPRO FZ45" data-dimension48="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="PIXPRO FZ45" data-dimension48="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="PIXPRO FZ55" data-dimension48="PIXPRO FZ55" 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="PIXPRO FZ55" data-dimension48="PIXPRO FZ55" 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="PIXPRO C1" data-dimension48="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="PIXPRO C1" data-dimension48="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="PIXPRO C1" data-dimension48="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="Ektar H35N Bundle" data-dimension48="Ektar H35N Bundle" 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><strong><a 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="Ektar H35N Bundle" data-dimension48="Ektar H35N Bundle" data-dimension25="$79">Ektar H35N Bundle: was $89 now $79</a></strong><br>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="Ektar H35N Bundle" data-dimension48="Ektar H35N Bundle" 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>
                                                                                                <dc:content><![CDATA[ <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="Mavic 4 Pro Fly More Combo" data-dimension48="Mavic 4 Pro Fly More Combo" 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><strong><a 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="Mavic 4 Pro Fly More Combo" data-dimension48="Mavic 4 Pro Fly More Combo" data-dimension25="$2749">Mavic 4 Pro Fly More Combo: was $2899 now $2749</a></strong><br>The <a href="https://www.tomsguide.com/cameras-photography/drones/dji-mavic-4-pro-review">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="Mavic 4 Pro Fly More Combo" data-dimension48="Mavic 4 Pro Fly More Combo" 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="Neo Fly More Combo" data-dimension48="Neo Fly More Combo" 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><strong><a 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="Neo Fly More Combo" data-dimension48="Neo Fly More Combo" data-dimension25="$295">Neo Fly More Combo: was $349 now $295</a></strong><br>A lightweight, compact and minimalistic drone, the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-neo-review">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="Neo Fly More Combo" data-dimension48="Neo Fly More Combo" 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="Avata 2 Fly More Combo" data-dimension48="Avata 2 Fly More Combo" 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><strong><a 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="Avata 2 Fly More Combo" data-dimension48="Avata 2 Fly More Combo" data-dimension25="$1019">Avata 2 Fly More Combo: was $1199 now $1019</a></strong><br>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">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="Avata 2 Fly More Combo" data-dimension48="Avata 2 Fly More Combo" 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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" 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><strong><a 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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" data-dimension25="$519">Atom 2 Fly More Combo: was $549 now $519</a></strong><br>The <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-2-review">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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" 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="X1" data-dimension48="X1" 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="X1" data-dimension48="X1" 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="X1" data-dimension48="X1" 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="A1" data-dimension48="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="A1" data-dimension48="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="A1" data-dimension48="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> ]]></dc:content>
                                                                                                                                            <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 & 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="Mavic 4 Pro Fly More Combo" data-dimension48="Mavic 4 Pro Fly More Combo" 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><strong><a 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="Mavic 4 Pro Fly More Combo" data-dimension48="Mavic 4 Pro Fly More Combo" data-dimension25="$2749">Mavic 4 Pro Fly More Combo: was $2899 now $2749</a></strong><br>The <a href="https://www.tomsguide.com/cameras-photography/drones/dji-mavic-4-pro-review">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="Mavic 4 Pro Fly More Combo" data-dimension48="Mavic 4 Pro Fly More Combo" 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="Neo Fly More Combo" data-dimension48="Neo Fly More Combo" 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><strong><a 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="Neo Fly More Combo" data-dimension48="Neo Fly More Combo" data-dimension25="$295">Neo Fly More Combo: was $349 now $295</a></strong><br>A lightweight, compact and minimalistic drone, the <a href="https://www.tomsguide.com/cameras-photography/drones/dji-neo-review">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="Neo Fly More Combo" data-dimension48="Neo Fly More Combo" 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="Avata 2 Fly More Combo" data-dimension48="Avata 2 Fly More Combo" 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><strong><a 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="Avata 2 Fly More Combo" data-dimension48="Avata 2 Fly More Combo" data-dimension25="$1019">Avata 2 Fly More Combo: was $1199 now $1019</a></strong><br>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">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="Avata 2 Fly More Combo" data-dimension48="Avata 2 Fly More Combo" 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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" 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><strong><a 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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" data-dimension25="$519">Atom 2 Fly More Combo: was $549 now $519</a></strong><br>The <a href="https://www.tomsguide.com/cameras-photography/drones/potensic-atom-2-review">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="Atom 2 Fly More Combo" data-dimension48="Atom 2 Fly More Combo" 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="X1" data-dimension48="X1" 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="X1" data-dimension48="X1" 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="X1" data-dimension48="X1" 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="A1" data-dimension48="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="A1" data-dimension48="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="A1" data-dimension48="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>
                                                                                                <dc:content><![CDATA[ <p>It's been quite an eventful few week for cameras. Insta360 launched a <a href="https://www.tomsguide.com/cameras-photography/groovy-baby-insta360-launches-latest-go-3s-bundle-inspired-by-retro-film-photography">Go 3S Retro Bundle</a>, we saw the <a href="https://www.tomsguide.com/cameras-photography/i-saw-the-gopro-mission-1-and-this-new-action-camera-could-make-all-point-and-shoots-obsolete-heres-why">GoPro Mission 1 lineup</a>, and on Wednesday May 13th, Sony and Canon dropped a camera each — what a time to be alive! Sony and Canon's  announcements came out of the blue, with teasers posted on their respective social channels either a week or just a few days prior to the launches.</p><p>Canon has expanded its V-series lineup of video-first cameras with the new EOS R6 V, while the Sony A7R VI succeeds the four-years-old <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/sony-a7r-v-review">A7R V</a>. Sony's offering sounds like the ultimate hybrid that builds on its predecessor's success, while Canon's latest camera looks like it could be the <a href="https://www.tomsguide.com/us/best-mirrorless-cameras,review-2221.html">best mirrorless camera</a> for video.</p><p>Needless to say, I'm excited and thrilled. As someone who reviews cameras for a living and loves photographing her surroundings, I'm intrigued by both cameras. But is there one I'm excited about more than the other? Well, that's what we're here to find out.</p><h2 id="a-quick-rundown-of-specs">A quick rundown of specs</h2><div ><table><thead><tr><th class="firstcol " ><p><strong>Specs</strong></p></th><th  ><p><strong>Sony A7R VI</strong></p></th><th  ><p>Canon EOS R6 V</p></th></tr></thead><tbody><tr><td class="firstcol " ><p><strong>Price</strong></p></td><td  ><p><a href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow">$4,498</a> / <a href="https://www.lcegroup.co.uk/New/Sony-A7R-VI-Mirrorless-Camera-Body_229893.html" target="_blank" rel="nofollow">£4,399</a></p></td><td  ><p><a href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow">$2,499</a> / <a href="https://www.lcegroup.co.uk/New/Canon-EOS-R6-V-Mirrorless-Camera-Body_229883.html" target="_blank" rel="nofollow">£2,399</a></p></td></tr><tr><td class="firstcol " ><p><strong>Sensor</strong></p></td><td  ><p>66.8MP Stacked Exmor RS BSI full frame</p></td><td  ><p>32.5MP CMOS full frame</p></td></tr><tr><td class="firstcol " ><p><strong>Processor</strong></p></td><td  ><p>BIONZ XR2</p></td><td  ><p>DIGIC X</p></td></tr><tr><td class="firstcol " ><p><strong>Stabilization</strong></p></td><td  ><p>Up to 8.5-stops IBIS</p></td><td  ><p>Up to 8.5-stops IBIS</p></td></tr><tr><td class="firstcol " ><p><strong>AF system</strong></p></td><td  ><p>Real-time Recognition AF+</p></td><td  ><p>Dual Pixel CMOS  II</p></td></tr><tr><td class="firstcol " ><p><strong>Viewfinder</strong></p></td><td  ><p>0.64-type Quad-XGA OLED, 9.44M dots, 120fps refresh rate</p></td><td  ><p>No</p></td></tr><tr><td class="firstcol " ><p><strong>Display</strong></p></td><td  ><p>3.2-inch 4-axis multi-angle LCD, 2.1M dots</p></td><td  ><p>3-inch vari-angle, 1.62M dots</p></td></tr><tr><td class="firstcol " ><p><strong>Max video resolution</strong></p></td><td  ><p>8K/30fps and 4K/120fps</p></td><td  ><p>7K/60fps RAW and 7K/30fps Open Gate</p></td></tr><tr><td class="firstcol " ><p><strong>Max shooting speed</strong></p></td><td  ><p>30fps</p></td><td  ><p>40fps</p></td></tr><tr><td class="firstcol " ><p><strong>Battery life (Stills, CIPA)</strong></p></td><td  ><p>600 shots (viewfinder)</p></td><td  ><p>640 shots</p></td></tr></tbody></table></div><h2 id="who-are-the-canon-eos-r6-v-and-sony-a7r-vi-for">Who are the Canon EOS R6 V and Sony A7R VI for?</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:1813px;"><p class="vanilla-image-block" style="padding-top:56.26%;"><img id="7mpPzNdW9KgsGjShTnvAH4" name="Canon-EOSR6V-1" alt="Canon EOS R6 V" src="https://cdn.mos.cms.futurecdn.net/7mpPzNdW9KgsGjShTnvAH4.jpg" mos="" align="middle" fullscreen="" width="1813" height="1020" attribution="" endorsement="" class="inline"></p></div></div><figcaption itemprop="caption description" class=" inline-layout"><span class="credit" itemprop="copyrightHolder">(Image credit: Canon)</span></figcaption></figure><p>Both the Canon EOS R6 V and the Sony A7R VI have different target audiences — but with some overlap. The A7R VI sets a new gold standard for high-resolution full frame cameras with its 66.8MP sensor and 30fps RAW burst capabilities, which make it great for all genres of photography, including wildlife, sports, and portrait. It also features 8K/30fps video so it sounds like a fantastic all-rounder.</p><p>Bolstering the A7R VI's position as a powerful hybrid camera is its OLED viewfinder with a 120fps refresh rate and 9.44M-dot resolution. You also get a 3.2-inch 4-axis LCD monitor with a 2.1M-dot resolution. Oh, and 8.5-stops of in-body image stabilization (IBIS) which means you can shoot at very slow shutter speeds and still walk away with a usable photo.</p><div class="product"><a data-dimension112="dc23baf3-0c00-47fe-b096-6a7fa2c51761" data-action="Deal Block" data-label="A7R VI" data-dimension48="A7R VI" data-dimension25="$4498" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="WZxeQLkZhoTgmrzRbKRu5L" name="A7RVI-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WZxeQLkZhoTgmrzRbKRu5L.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony A7R VI is a professional-grade hybrid camera that utilizes a newly developed 66.8MP sensor with 30fps RAW burst shooting and can shoot 8K/30fps video. It also offers up to 8.5-stops of IBIS, and packs a high-res EVF as well as a 4-axis LCD monitor.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow" data-dimension112="dc23baf3-0c00-47fe-b096-6a7fa2c51761" data-action="Deal Block" data-label="A7R VI" data-dimension48="A7R VI" data-dimension25="$4498">View Deal</a></p></div><p>And what about the EOS R6 V? It's the second R-V body Canon has introduced, and it joins the excellent <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r50-v-review">Canon EOS R50 V</a>. The 'V' version of the EOS R6 is also the <em>fourth </em>EOS R6 body in existence — and as the 'V' suggests, it's a video-first take on Canon's hybrid camera. There's no viewfinder so you must rely on the camera's 3-inch vari-angle screen and its 1.62M-dot resolution. The EOS R6 V offers 7K/60fps RAW and 7K/30fps Open Gate video capabilities, both of which make it ideal for content creation.</p><div class="product"><a data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="EOS R6 V" data-dimension48="EOS R6 V" data-dimension25="$2499" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="p5QzTEnfDQHjKyd6QWp2E4" name="EOSR6V-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/p5QzTEnfDQHjKyd6QWp2E4.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Canon EOS R6 V is a compact video-first full frame camera for content creators, and it features a 32.5MP sensor, 7K/60fps RAW and 7K/30fps Open Gate capabilities. It utilizes Canon's excellent Dual Pixel CMOS II autofocus system and up to 8.5-stops of IBIS too.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow" data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="EOS R6 V" data-dimension48="EOS R6 V" data-dimension25="$2499">View Deal</a></p></div><p>In addition to that, you get 40fps continuous shooting which is... surprising, and something I wasn't really expecting in a video-first camera. While this camera sounds like it would be peerless for video, I'm sure it will be ideal for capturing photos of birds in flight too with its 32.5MP CMOS sensor and best-in-class Dual Pixel CMOS II autofocus system. And did I mention it packs up to 8.5-stops of IBIS, just like the A7R VI? Yep, it's almost got it all.</p><h2 id="the-one-i-m-most-excited-for-and-why">The one I'm most excited for (and why)</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:800px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9kA4YPjZVRCKMM4vA8LnF4" name="Canon-EOSR6V-2" alt="Canon EOS R6 V" src="https://cdn.mos.cms.futurecdn.net/9kA4YPjZVRCKMM4vA8LnF4.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><div data-widget-type="multimodelreview" data-model-name="Sony A7R VI,Canon EOS R6 V"></div> ]]></dc:content>
                                                                                                                                            <link>https://www.tomsguide.com/cameras-photography/mirrorless-cameras/the-canon-eos-r6-v-and-the-sony-a7r-vi-dropped-this-week-both-look-incredible-but-heres-the-one-im-most-excited-for-and-why</link>
                                                                            <description>
                            <![CDATA[ The Canon EOS R6 V and the Sony A7R VI are here, and both cameras sound powerful and offer best-in-class features. But there's one camera I'm more excited for. ]]>
                                                                                                            </description>
                                                                                                                                <guid isPermaLink="false">UAJpDjwJcWMv6KVG2RhuF6</guid>
                                                                                                <enclosure url="https://cdn.mos.cms.futurecdn.net/RNWmMtFfENFb7ADz2uZgbG-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 & 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="A7R VI" data-dimension48="A7R VI" data-dimension25="$4498" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="WZxeQLkZhoTgmrzRbKRu5L" name="A7RVI-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/WZxeQLkZhoTgmrzRbKRu5L.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Sony A7R VI is a professional-grade hybrid camera that utilizes a newly developed 66.8MP sensor with 30fps RAW burst shooting and can shoot 8K/30fps video. It also offers up to 8.5-stops of IBIS, and packs a high-res EVF as well as a 4-axis LCD monitor.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970580-REG/sony_ilce7rm6b_a7r_vi_mirrorless_camera.html" target="_blank" rel="nofollow" data-dimension112="dc23baf3-0c00-47fe-b096-6a7fa2c51761" data-action="Deal Block" data-label="A7R VI" data-dimension48="A7R VI" data-dimension25="$4498">View Deal</a></p></div><p>And what about the EOS R6 V? It's the second R-V body Canon has introduced, and it joins the excellent <a href="https://www.tomsguide.com/cameras-photography/mirrorless-cameras/canon-eos-r50-v-review">Canon EOS R50 V</a>. The 'V' version of the EOS R6 is also the <em>fourth </em>EOS R6 body in existence — and as the 'V' suggests, it's a video-first take on Canon's hybrid camera. There's no viewfinder so you must rely on the camera's 3-inch vari-angle screen and its 1.62M-dot resolution. The EOS R6 V offers 7K/60fps RAW and 7K/30fps Open Gate video capabilities, both of which make it ideal for content creation.</p><div class="product"><a data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="EOS R6 V" data-dimension48="EOS R6 V" data-dimension25="$2499" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow"><figure class="van-image-figure "  ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:500px;"><p class="vanilla-image-block" style="padding-top:100.00%;"><img id="p5QzTEnfDQHjKyd6QWp2E4" name="EOSR6V-deal" caption="" alt="" src="https://cdn.mos.cms.futurecdn.net/p5QzTEnfDQHjKyd6QWp2E4.jpg" mos="" align="middle" fullscreen="" width="500" height="500" attribution="" endorsement="" credit="" class=""></p></div></div></figure></a><p>The Canon EOS R6 V is a compact video-first full frame camera for content creators, and it features a 32.5MP sensor, 7K/60fps RAW and 7K/30fps Open Gate capabilities. It utilizes Canon's excellent Dual Pixel CMOS II autofocus system and up to 8.5-stops of IBIS too.<a class="view-deal button" href="https://www.bhphotovideo.com/c/product/1970465-REG/canon_7250c002_eos_r6_v_mirrorless.html" target="_blank" rel="nofollow" data-dimension112="017f9ea4-9c85-4f62-a759-15c236ba35d9" data-action="Deal Block" data-label="EOS R6 V" data-dimension48="EOS R6 V" data-dimension25="$2499">View Deal</a></p></div><p>In addition to that, you get 40fps continuous shooting which is... surprising, and something I wasn't really expecting in a video-first camera. While this camera sounds like it would be peerless for video, I'm sure it will be ideal for capturing photos of birds in flight too with its 32.5MP CMOS sensor and best-in-class Dual Pixel CMOS II autofocus system. And did I mention it packs up to 8.5-stops of IBIS, just like the A7R VI? Yep, it's almost got it all.</p><h2 id="the-one-i-m-most-excited-for-and-why">The one I'm most excited for (and why)</h2><figure class="van-image-figure  inline-layout" data-bordeaux-image-check ><div class='image-full-width-wrapper'><div class='image-widthsetter' style="max-width:800px;"><p class="vanilla-image-block" style="padding-top:56.25%;"><img id="9kA4YPjZVRCKMM4vA8LnF4" name="Canon-EOSR6V-2" alt="Canon EOS R6 V" src="https://cdn.mos.cms.futurecdn.net/9kA4YPjZVRCKMM4vA8LnF4.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><div data-widget-type="multimodelreview" data-model-name="Sony A7R VI,Canon EOS R6 V"></div>
                                                            </article>
                            ]]>
                        </content:encoded>
                                                </item>
            </channel>
</rss>