PageSourceSearch

https://vecanis.se/hundmat

html vecanis.se collected 2026-10-08 05:46:19 UTC 511,264 bytes, 12,503 lines download raw bytes

1<!DOCTYPE html>
2<html lang="sv">
3
4<head>
5<link rel="alternate" href="https://vecanis.se/hundmat" hreflang="sv" />
6<link rel="alternate" href="https://vecanis.se/en/hundmat" hreflang="en" />
7<meta property="ix:host" content="cdn.quickbutik.com/images">
8  <title>Växtbaserad hundmat - ett Hypoallergent och klimatsmart val - Vecanis</title>
9  <meta charset="utf-8">
10
11  <meta name="author" content="Quickbutik">
12  <meta name="description" content="Allergivänlig, växtbaserad och klimatvänlig hundmat som är både god &amp; näringsrik- för en hälsosammare hund och planet. 🌱🐶">
13
14  <meta property="og:description" content="Allergivänlig, växtbaserad och klimatvänlig hundmat som är både god &amp; näringsrik- för en hälsosammare hund och planet. 🌱🐶">
15  <meta property="og:title" content="Växtbaserad hundmat - ett Hypoallergent och klimatsmart val - Vecanis">
16  <meta property="og:url" content="https://vecanis.se/hundmat">
17  <meta property="og:site_name" content="Vecanis">
18  <meta property="og:type" content="website">
19  <meta property="og:image" content="">
20
21  <meta property="twitter:card" content="summary">
22  <meta property="twitter:title" content="Vecanis">
23  <meta property="twitter:description" content="Allergivänlig, växtbaserad och klimatvänlig hundmat som är både god &amp; näringsrik- för en hälsosammare hund och planet. 🌱🐶">
24  <meta property="twitter:image" content="">
25
26  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
27
28  <link rel="canonical" href="https://vecanis.se/hundmat">
29
30  <!-- Google Fonts -->
31  
32  
33  
34  
35  
36  
37
38  <!-- Preload images -->
39  <link rel="preload" as="image" href="https://cdn.quickbutik.com/images/2666L/templates/lua/assets/logo.png?s=96971&auto=format&fit=clip&h=160&w=600">
40  
41  <!-- Favicon -->
42  <link rel="icon" type="image/png" href="https://cdn.quickbutik.com/images/2666L/templates/lua/assets/favicon.png?s=96971&auto=format">
43
44  <!-- Components -->
45  
45<script defer src="https://storage.quickbutik.com/templates/lua/js/settings.js?96971"></script>
45
46  
46<script defer src="https://storage.quickbutik.com/templates/lua/js/tailwind-styling.js?96971"></script>
46
47
48  <!-- Quickbutik -->
49  
50<script type="text/javascript">var qs_store_url = "https://vecanis.se";</script>
vendor: 1 bytes, line 50
50
51<script type="text/javascript">var qs_store_apps_data = {"location":"\/hundmat","products":"989,984,982,979,935,934,896,881,880,810,809,805,793,788,787,772,771,770,769,763,740,733,732,694,673,672,671,670,665,643,633,629,619,362,361,360,134,57,56,55,54,15,13,12,5","product_id":null,"category_id":"2","category_name":null,"fetch":"fetch","order":null,"search":null,"recentPurchaseId":null,"pagination":{"total_page_count":null,"current_page":null,"total_product_count":null,"previous_page":null,"next_page":null,"previous_page_url":null,"next_page_url":null,"current_url":"https:\/\/vecanis.se\/hundmat","has_more_pages":false,"pagination_activated":false,"sort":"pid_0"}}; var qs_store_apps = [];</script>
vendor: 1 bytes, line 51
51
52<script defer type="text/javascript" src="/assets/qb_essentials_alpine.js?v=20260819a"></script>
52
53
54  
54<script defer src="https://storage.quickbutik.com/templates/lua/js/appsOverrides.js?96971"></script>
54
55  
56  <!-- Libraries -->
57  
57<script defer type="text/javascript" src="/assets/imgix.min.js?v=290323"></script>
57
58  
58<script defer src="https://storage.quickbutik.com/templates/lua/js/swiper-bundle.min.js?96971"></script>
58
59  
59<script defer src="https://storage.quickbutik.com/templates/lua/js/sliders.js?96971"></script>
59
60  
60<script defer src="https://storage.quickbutik.com/templates/lua/js/photoswipe-lightbox.umd.min.js?96971"></script>
60
61  
61<script defer src="https://storage.quickbutik.com/templates/lua/js/photoswipe.umd.min.js?96971"></script>
61
62
63  <!-- Default parameters for Imgix -->
64  
64<script>document.addEventListener("DOMContentLoaded", () => { imgix.config.defaultParams = { auto: 'format' } })</script>
64
65
66  <!-- Scripts in head -->  
67	  <link rel="icon" type="image/png" href="https://s3-eu-west-1.amazonaws.com/storage.quickbutik.com/stores/2666L/files/favicon.ico">
68
69
70
71  <!-- Stylesheet -->
72  <style>[x-cloak]{display: none !important;}img[ix-sizes="auto"]{display:block;width:100%;}img[src=""],img[src="#"],img:not([src]){visibility: hidden;}</style>
73  <link rel="preload" href="https://storage.quickbutik.com/templates/lua/css/swiper-bundle.min.css?96971" as="style" onload="this.onload=null;this.rel='stylesheet'">
74  <noscript><link rel="stylesheet" href="https://storage.quickbutik.com/templates/lua/css/swiper-bundle.min.css?96971"></noscript>
75  <link rel="preload" href="https://storage.quickbutik.com/templates/lua/css/photoswipe.css?96971" as="style" onload="this.onload=null;this.rel='stylesheet'">
76  <noscript><link rel="stylesheet" href="https://storage.quickbutik.com/templates/lua/css/photoswipe.css?96971"></noscript>
77  <link type="text/css" rel="stylesheet" href="https://storage.quickbutik.com/stores/2666L/templates/lua/css/theme.css?96971">
78<script type="text/javascript" defer>document.addEventListener('DOMContentLoaded', () => {
79    document.body.setAttribute('data-qb-page', 'list');
80});</script>
80<script type="text/javascript" defer>(function () {
81  const MAX_GET_QUERY_LENGTH = 2000;
82
83  window.apps = window.apps ?? {};
84
85  window.registerApp = function (app) {
86    console.debug('registerApp', app);
87    window.apps[app.identifier] = app;
88  };
89
90  document.addEventListener('DOMContentLoaded', () => {
91    const page = document.body.dataset?.qbPage;
92    const bodyFormData = new FormData();
93
94    const productIds = [
95      ...new Set(
96        [...document.querySelectorAll('[data-qb-selector="product-item"]')]
97          .map((product) => product.dataset.productId?.trim())
98          .filter(Boolean)
99      ),
100    ];
101
102    bodyFormData.set('location', window.location.pathname + window.location.search);
103    bodyFormData.set('products', productIds.join(','));
104    bodyFormData.set(
105      'category_id',
106      document.querySelector('[data-qb-category-id]')?.dataset?.qbCategoryId || ''
107    );
108    bodyFormData.set(
109      'product_id',
110      ['product'].includes(page) && document.querySelector('[data-product-id]')?.dataset?.productId || ''
111    );
112
113    bodyFormData.set('fetch', 'fetch');
114    bodyFormData.set('order', '');
115    bodyFormData.set('search', '');
116    bodyFormData.set('recentPurchaseId', '');
117
118    const visitedProductId = bodyFormData.get('product_id');
119    if (visitedProductId) {
120      const visit = new URLSearchParams({ product_id: visitedProductId });
121      if (!(navigator.sendBeacon && navigator.sendBeacon(qs_store_url + '/apps/visit', visit))) {
122        fetch(qs_store_url + '/apps/visit', { method: 'post', body: visit, keepalive: true }).catch(() => {});
123      }
124    }
125
126    const query = [...bodyFormData.entries()]
127      .map(([key, value]) =>
128        encodeURIComponent(key) + '=' +
129        (key === 'products' ? productIds.map(encodeURIComponent).join(',') : encodeURIComponent(value))
130      )
131      .join('&');
132
133    const postApps = () => {
134      const postData = new FormData();
135      bodyFormData.forEach((value, key) => postData.append(key, value));
136      postData.set('variants', JSON.stringify(typeof qs_options !== 'undefined' ? qs_options : []));
137
138      return fetch(qs_store_url + '/apps/fetch', { method: 'post', body: postData }).then((res) => res.json());
139    };
140
141    const appsResponse =
142      query.length <= MAX_GET_QUERY_LENGTH
143        ? fetch(qs_store_url + '/apps/fetch?' + query, { method: 'get' })
144            .then((res) => {
145              if (!res.ok || !res.headers.get('X-QB-Apps-Fetch')) {
146                throw new Error('GET /apps/fetch not accepted (' + res.status + ')');
147              }
148
149              return res.json();
150            })
151            .catch(postApps)
152        : postApps();
153
154    appsResponse
155      .then((data) => {
156        console.debug('Fetch apps success', data);
157
158        if (typeof Alpine === 'undefined') {
159          document.addEventListener('alpine:init', () => {
160            initApps(data);
161          })
162        } else {
163          initApps(data);
164        }
165      })
166      .catch((error) => {
167        console.debug('Fetch apps error', error);
168      });
169  });
170
171  function initApps(appData) {
172    const page = document.querySelector('[data-qb-page]')?.dataset?.qbPage;
173
174    console.debug('initApps', appData, page, window.apps);
175
176    if (page) {
177      Object.keys(window.apps).forEach((appIdentifier) => {
178        if (window.apps[appIdentifier].pages.includes(page)) {
179          let appOverrides = window?.appsOverrides?.[appIdentifier];
180
181          if (appOverrides) {
182            window.apps[appIdentifier] = merge(
183              window.apps[appIdentifier],
184              appOverrides
185            );
186          }
187
188          window.apps[appIdentifier].onMount({ appData, page });
189        }
190      });
191    }
192  }
193
194  function isObject(obj) {
195    return obj !== null && typeof obj === 'object';
196  }
197
198  function merge(target, source) {
199    if (!isObject(target) || !isObject(source)) {
200      throw new Error('Both target and source must be objects.');
201    }
202
203    for (const key in source) {
204      if (source.hasOwnProperty(key)) {
205        const targetValue = target[key];
206        const sourceValue = source[key];
207
208        if (isObject(sourceValue) && isObject(targetValue)) {
209          target[key] = merge(targetValue, sourceValue);
210        } else {
211          target[key] = sourceValue;
212        }
213      }
214    }
215
216    return target;
217  }
218})();
219</script>
219<script src="/assets/shopassets/cookieconsent/cookieconsent.js?v=230"></script>
219
220<style>
221    :root {
222        --cc-btn-primary-bg: #222222 !important;
223        --cc-btn-primary-text: #fff !important;
224        --cc-btn-primary-hover-bg: #222222 !important;
225        --cc-btn-primary-hover-text: #fff !important;
226    }
227</style>
228<style>
229    html.force--consent,
230    html.force--consent body{
231        height: auto!important;
232        width: 100vw!important;
233    }
234</style>
235
236    
236<script type="text/javascript">
237    var qb_cookieconsent = initCookieConsent();
238    var qb_cookieconsent_blocks = [{"title":"Vi anv\u00e4nder cookies","description":"Vecanis anv\u00e4nder cookies - inte fr\u00e5n ugnen, utan de digitala. Genom att forts\u00e4tta p\u00e5 sidan godk\u00e4nner du anv\u00e4ndandet av cookies."},{"title":"N\u00f6dv\u00e4ndiga cookies *","description":"Dessa cookies \u00e4r n\u00f6dv\u00e4ndiga f\u00f6r att butiken ska fungera korrekt och g\u00e5r inte att st\u00e4nga av utan att beh\u00f6va l\u00e4mna denna webbplats. De anv\u00e4nds f\u00f6r att funktionalitet som t.ex. varukorg, skapa ett konto och annat ska fungera korrekt.","toggle":{"value":"basic","enabled":true,"readonly":true}},{"title":"Statistik och prestanda","description":"Dessa cookies g\u00f6r att vi kan r\u00e4kna bes\u00f6k och trafik och d\u00e4rmed ta reda p\u00e5 saker som vilka sidor som \u00e4r mest popul\u00e4ra och hur bes\u00f6karna r\u00f6r sig p\u00e5 v\u00e5r webbplats. All information som samlas in genom dessa cookies \u00e4r sammanst\u00e4lld, vilket inneb\u00e4r att den \u00e4r anonym. Om du inte till\u00e5ter dessa cookies kan vi inte ge dig en lika skr\u00e4ddarsydd upplevelse.","toggle":{"value":"analytics","enabled":false,"readonly":false},"cookie_table":[{"col1":"_ga","col2":"google.com","col3":"2 years","col4":"Google Analytics"},{"col1":"_gat","col2":"google.com","col3":"1 minute","col4":"Google Analytics"},{"col1":"_gid","col2":"google.com","col3":"1 day","col4":"Google Analytics"}]},{"title":"Marknadsf\u00f6ring","description":"Dessa kakor s\u00e4tts via v\u00e5r webbplats av v\u00e5ra annonspartners f\u00f6r att skapa en profil utifr\u00e5n dina intressen och visa relevanta annonser p\u00e5 andra webbplatser. De kan samla information om ditt surfbeteende och anv\u00e4ndas f\u00f6r att identifiera din webbl\u00e4sare och internetenhet. Vissa annonspartners kan \u00e4ven ta emot hashad kontaktinformation (till exempel e-postadress) f\u00f6r att f\u00f6rb\u00e4ttra annonsm\u00e5lgrupperingen. Om du inte till\u00e5ter dessa kakor kommer du att uppleva mindre riktad annonsering.","toggle":{"value":"marketing","enabled":false,"readonly":false}}];
239</script>
239
240<link rel="stylesheet" href="https://storage.quickbutik.com/stores/2666L/templates/lua/css/tailwind.css?96971" />    <!-- Google Tag Manager -->
241    
241<script>
242    window.dataLayer = window.dataLayer || [];
243    function gtag(){dataLayer.push(arguments);}
244
245    gtag('consent', 'default', {
246        'analytics_storage': 'denied',
247        'ad_storage': 'denied',
248        'ad_user_data': 'denied',
249        'ad_personalization': 'denied',
250        'wait_for_update': 500,
251    });
252
253    // Update consent if already granted (returning visitor with cc_cookie)
254    (function(){
255        try {
256            var m = document.cookie.match(/(?:^|;\s*)cc_cookie=([^;]+)/);
257            if (m) {
258                var c = JSON.parse(decodeURIComponent(m[1]));
259                if (c && c.level) {
260                    var hasAnalytics = c.level.indexOf('analytics') !== -1;
261                    var hasMarketing = c.level.indexOf('marketing') !== -1;
262                    if (hasAnalytics || hasMarketing) {
263                        gtag('consent', 'update', {
264                            'analytics_storage': hasAnalytics ? 'granted' : 'denied',
265                            'ad_storage': hasMarketing ? 'granted' : 'denied',
266                            'ad_user_data': hasMarketing ? 'granted' : 'denied',
267                            'ad_personalization': hasMarketing ? 'granted' : 'denied',
268                        });
269                    }
270                }
271            }
272        } catch(e) {}
273    })();
274
275    // Initialize GTM
vendor: 348 bytes, lines 275-280
275
276    (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
277    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
278    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
279    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
280    })(window,document,'script','dataLayer','
280GTM-WTRG25FS
vendor: 9 bytes, lines 280-282
280');
281
282    
282// Handle consent updates (user clicks Accept)
283    document.addEventListener('QB_CookieConsentAccepted', function(event) {
284        var cookie = qbGetEvent(event).cookie;
285        var getConsentLevel = function(level) { return (cookie && cookie.level && cookie.level.includes(level)) ? 'granted' : 'denied'; };
286
287        gtag('consent', 'update', {
288            'analytics_storage': getConsentLevel('analytics'),
289            'ad_storage': getConsentLevel('marketing'),
290            'ad_user_data': getConsentLevel('marketing'),
291            'ad_personalization': getConsentLevel('marketing'),
292        });
293    });
294    </script>
294
295    <!-- End Google Tag Manager -->
296
297    <!-- Google Tag Manager (noscript) -->
vendor: 72 bytes, lines 297-298
297
298    <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
298GTM-WTRG25FS
vendor: 91 bytes, lines 298-300
298"
299    height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
300    
300<!-- End Google Tag Manager (noscript) --></head>
301
302<body class="antialiased bg-body text-body font-body">
303      <section
304        data-qb-area="header" data-qb-element=""
305        x-data="{navMenuOpen: false}"
306        @keydown.escape="navMenuOpen = false; $store.cart.hideCart(); $store.settings.hideSettings();"
307        class="bg-header md:pb-4">
308      
309        <section x-data class="w-full" data-qb-element="notifications">
310          <template x-for="notification in $store.notifications.notifications" :key="notification.id">
311            <div x-show="notification.visible" x-on:click="$store.notifications.removeNotification(notification.id)"
312            x-transition:enter='transition ease-out duration-default transform'
313            x-transition:enter-start='opacity-0 -translate-y-4'
314            x-transition:enter-end='opacity-100 translate-y-0'
315            x-transition:leave='transition ease-in duration-default transform'
316            x-transition:leave-start='opacity-100 translate-y-0'
317            x-transition:leave-end='opacity-0 -translate-y-4'
318            >
319              <div class="relative w-full px-4 sm:px-8 py-4 bg-black" :style="{'background-color': notification.backgroundColor || '#000'}">
320                <div class="relative z-10 flex items-center justify-between -m-2">
321                  <div class="flex-1 p-2" :style="{'color': notification.textColor || '#fff'}">
322                    <template x-if="notification.message">
323                      <h3 class="font-heading font-semibold text-lg text-center" x-html="notification.message"></h3>
324                    </template>
325                    <template x-if="notification.htmlContent">
326                      <div class="text-center" x-html="notification.htmlContent"></div>
327                    </template>
328                  </div>
329                  <div class="p-2">
330                    <button
331                      class="w-8 h-8 flex items-center justify-center rounded-full hover:opacity-75 transition-opacity duration-200 cursor-pointer"
332                      :style="{'color': notification.textColor || '#fff'}"
333                      x-on:click.stop="$store.notifications.removeNotification(notification.id)"
334                      aria-label="Close notification"
335                    >
336                      <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
337                        <path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
338                      </svg>
339                    </button>
340                  </div>
341                </div>
342              </div>
343            </div>
344          </template>
345        </section>
346      
347        
348        <div class="flex items-center justify-between relative py-2" style="">
349          <div class="container px-4 mx-auto">
350            <div class="flex items-center justify-between relative">
351              <div class="flex text-sm">
352                
353                <!-- Tax -->
354      
355              </div>
356              <div class="flex text-sm">
357                
358                <!-- Settings -->
359                
360                <button @click="$store.settings.showSettings()" aria-label="Inställningar" class="flex items-center opacity-70 hover:opacity-100 mr-4 focus:outline-none" style="">
361                  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 opacity-50">
362                    <path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
363                    <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
364                  </svg>
365                  <span class="hidden sm:inline-block ml-1">Inställningar</span>
366                </button>
367      
368                <!-- Login -->
369                <a href="/customer/login" class="flex items-center opacity-70 hover:opacity-100">
370                  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 opacity-50">
371                    <path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"></path>
372                  </svg>
373                  <span class="hidden sm:inline-block ml-1">Logga in</span>
374                </a>
375      
376              </div>
377            </div>
378          </div>
379        </div>
380      
381        <hr class="border-foreground opacity-5">
382      
383        <nav>
384          <div class="container px-4 py-2 sm:py-6 mx-auto">
385            <div class="flex items-center justify-between relative">
386      
387              <!-- Menu button -->
388              <div class="flex justify-items-start w-2/5">
389      
390                <div class="lg:hidden flex justify-items-start w-1/5">
391                  <button @click="navMenuOpen = !navMenuOpen" aria-label="Menu"
392                    class="navbar-burger py-3 px-2 sm:px-4 hover:text-primary hover:bg-black/5 transition duration-default focus:outline-none">
393                    <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 sm:w-7 sm:h-7">
394                      <path fill-rule="evenodd" clip-rule="evenodd"
395                        d="M3.5 7C3.22386 7 3 6.77614 3 6.5C3 6.22386 3.22386 6 3.5 6H20.5C20.7761 6 21 6.22386 21 6.5C21 6.77614 20.7761 7 20.5 7H3.5ZM3.5 12C3.22386 12 3 11.7761 3 11.5C3 11.2239 3.22386 11 3.5 11H20.5C20.7761 11 21 11.2239 21 11.5C21 11.7761 20.7761 12 20.5 12H3.5ZM3 16.5C3 16.7761 3.22386 17 3.5 17H20.5C20.7761 17 21 16.7761 21 16.5C21 16.2239 20.7761 16 20.5 16H3.5C3.22386 16 3 16.2239 3 16.5Z"
396                        fill="currentColor"></path>
397                    </svg>
398                  </button>
399                </div>
400      
401              </div>
402      
403              <!-- Logo -->
404              <a class="inline-block h-16" href="https://vecanis.se">
405                  <img ix-sizes="auto" ix-src="https://cdn.quickbutik.com/images/2666L/templates/lua/assets/logo.png?s=96971&auto=format&fit=clip&h=160&w=400&dpr=2" alt="Vecanis" class="object-center object-contain sm:px-4 h-16">
406              </a>
407      
408              <div class="flex justify-end w-2/5">
409      
410                <!-- Search -->
411                <button @click="$store.search.showSearch(); $nextTick(() => document.getElementById('searchInput').focus());" aria-label="Sök" class="py-2 px-2 rounded-full hover:bg-black/5 hover:text-primary transition duration-default focus:outline-none">
412                  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 sm:w-6 sm:h-6">
413                    <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
414                  </svg>
415                </button>
416      
417                <!-- Wishlist -->
418      
419                <!-- Cart -->
420                <button @click="$store.cart.toggleCart()" aria-label="Varukorg" class="relative py-2 px-2 rounded-full hover:bg-black/5 hover:text-primary transition duration-default focus:outline-none">
421                  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 sm:w-6 sm:h-6">
422                    <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 00-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 00-16.536-1.84M7.5 14.25L5.106 5.272M6 20.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm12.75 0a.75.75 0 11-1.5 0 .75.75 0 011.5 0z" />
423                  </svg>
424      
425                  <!-- Cart bubble -->
426                  <template x-if="$store.cart.totalQuantity > 0">
427                    <span class="cart-bubble absolute flex items-center justify-center -top-0.5 -right-0.5 h-6 w-6 border-2 border-header bg-primary text-white font-bold text-xs rounded-full scale-75 md:scale-100" x-text="$store.cart.totalQuantity"></span>
428                  </template>
429      
430                </button>
431              </div>
432      
433            </div>
434          </div>
435          <div class="hidden lg:block container px-4 mx-auto">
436            <div>
437              
438              <ul x-data="{ navbarLevelsDepth: 3 }" class="flex justify-center">
439            
440            
441            
442                    <li x-data="dropdown" @mouseover="open = navbarLevelsDepth >= 1 && true" @mouseleave="open = false" class=" mx-2 lg:mx-4 group">
443                      <div class="relative flex items-center justify-center">
444                        <a href="/hundmat" id="menu-item-8"
445                          class="flex py-2 px-1 transition ease-in-out duration-default font-bold hover:opacity-70 active">
446                          <span>Hundmat</span>
447                        </a>
448                        <svg x-show="navbarLevelsDepth >= 1"  xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
449                            stroke-width="1.5" stroke="currentColor"
450                            class="w-4 h-4 cursor-pointer transform duration-default rotate-90 " :class="{'rotate-0' : open}">
451                            <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
452                        </svg>
453                      </div>
454            
455                      <!-- First drop down -->
456                      <ul x-cloak
457                        x-show="open"
458                        "
459                        x-transition:enter='transition ease-out transform'
460                        x-transition:enter-start='opacity-0 -translate-y-4'
461                        x-transition:enter-end='opacity-100 translate-y-0'
462                        x-transition:leave='transition ease-in transform'
463                        x-transition:leave-start='opacity-100 translate-y-0'
464                        x-transition:leave-end='opacity-0 -translate-y-4'
465                        class="absolute z-50 w-full max-w-xs px-6 py-2 bg-header shadow-md roundednone duration-default" aria-labelledby="menu-item-8">
466            
467            
468                            <li><a href="/hundmat/topping" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Topping</a></li>
469            
470            
471            
472                            <li><a href="/hundmat/blotmat" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Blötmat</a></li>
473            
474            
475            
476                            <li><a href="/hundmat/torrfoder" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Torrfoder</a></li>
477            
478            
479            
480                            <li><a href="/hundmat/allergivanlig-hundmat" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Allergi</a></li>
481            
482            
483            
484                            <li><a href="/hundmat/glutenfritt" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Glutenfritt</a></li>
485            
486            
487            
488                            <li><a href="/hundmat/fettsnalt" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Fettsnålt</a></li>
489            
490            
491            
492                            <li><a href="/hundmat/spannmalsfritt" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Spannmålsfritt</a></li>
493            
494            
495            
496                            <li><a href="/hundmat/senior" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Senior</a></li>
497            
498            
499            
500                            <li><a href="/hundmat/valp" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Valp</a></li>
501            
502            
503            
504                            <li><a href="/hundmat/mini" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Mini</a></li>
505            
506                      </ul>
507                    </li>
508            
509            
510            
511                    <li x-data="dropdown" @mouseover="open = navbarLevelsDepth >= 1 && true" @mouseleave="open = false" class=" mx-2 lg:mx-4 group">
512                      <div class="relative flex items-center justify-center">
513                        <a href="/hundgodis" id="menu-item-146"
514                          class="flex py-2 px-1 transition ease-in-out duration-default  hover:opacity-70 ">
515                          <span>Hundgodis</span>
516                        </a>
517                        <svg x-show="navbarLevelsDepth >= 1"  xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
518                            stroke-width="1.5" stroke="currentColor"
519                            class="w-4 h-4 cursor-pointer transform duration-default rotate-90 " :class="{'rotate-0' : open}">
520                            <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
521                        </svg>
522                      </div>
523            
524                      <!-- First drop down -->
525                      <ul x-cloak
526                        x-show="open"
527                        "
528                        x-transition:enter='transition ease-out transform'
529                        x-transition:enter-start='opacity-0 -translate-y-4'
530                        x-transition:enter-end='opacity-100 translate-y-0'
531                        x-transition:leave='transition ease-in transform'
532                        x-transition:leave-start='opacity-100 translate-y-0'
533                        x-transition:leave-end='opacity-0 -translate-y-4'
534                        class="absolute z-50 w-full max-w-xs px-6 py-2 bg-header shadow-md roundednone duration-default" aria-labelledby="menu-item-146">
535            
536            
537                            <li><a href="/hundgodis/tuggben" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Tuggben</a></li>
538            
539            
540            
541                            <li><a href="/hundgodis/beloningsgodis" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Belöningsgodis</a></li>
542            
543            
544            
545                            <li><a href="/hundgodis/allergi-hundgodis" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Allergi</a></li>
546            
547            
548            
549                            <li><a href="/hundgodis/glutenfritt-hundgodis" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Glutenfritt</a></li>
550            
551            
552            
553                            <li><a href="/hundgodis/fettsnalt-hundgodis" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Fettsnålt</a></li>
554            
555            
556            
557                            <li><a href="/hundgodis/spannmalsfritt-hundgodis" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Spannmålsfritt</a></li>
558            
559            
560            
561                            <li><a href="/hundgodis/dentalgodis" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Dentalgodis</a></li>
562            
563            
564            
565                            <li><a href="/hundgodis/seniorhundgodis" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Senior</a></li>
566            
567            
568            
569                            <li><a href="/hundgodis/valp-hundgodis" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Valp</a></li>
570            
571            
572            
573                            <li><a href="/hundgodis/mini" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Mini</a></li>
574            
575            
576            
577                            <li><a href="/hundgodis/jumbo" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Jumbo</a></li>
578            
579            
580            
581                            <li><a href="/ovrigt" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Övrigt</a></li>
582            
583                      </ul>
584                    </li>
585            
586            
587            
588                    <li x-data="dropdown" @mouseover="open = navbarLevelsDepth >= 1 && true" @mouseleave="open = false" class=" mx-2 lg:mx-4 group">
589                      <div class="relative flex items-center justify-center">
590                        <a href="/tillskott" id="menu-item-105"
591                          class="flex py-2 px-1 transition ease-in-out duration-default  hover:opacity-70 ">
592                          <span>Tillskott</span>
593                        </a>
594                        <svg x-show="navbarLevelsDepth >= 1"  xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
595                            stroke-width="1.5" stroke="currentColor"
596                            class="w-4 h-4 cursor-pointer transform duration-default rotate-90 " :class="{'rotate-0' : open}">
597                            <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
598                        </svg>
599                      </div>
600            
601                      <!-- First drop down -->
602                      <ul x-cloak
603                        x-show="open"
604                        "
605                        x-transition:enter='transition ease-out transform'
606                        x-transition:enter-start='opacity-0 -translate-y-4'
607                        x-transition:enter-end='opacity-100 translate-y-0'
608                        x-transition:leave='transition ease-in transform'
609                        x-transition:leave-start='opacity-100 translate-y-0'
610                        x-transition:leave-end='opacity-0 -translate-y-4'
611                        class="absolute z-50 w-full max-w-xs px-6 py-2 bg-header shadow-md roundednone duration-default" aria-labelledby="menu-item-105">
612            
613            
614                            <li><a href="/tillskott/26-munhygien" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Munhygien</a></li>
615            
616            
617            
618                            <li><a href="/tillskott/pals-hud" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Päls &amp; Hud</a></li>
619            
620            
621            
622                            <li><a href="/tillskott/leder-rorlighet" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Leder &amp; Rörlighet</a></li>
623            
624            
625            
626                            <li><a href="/tillskott/mage" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Mage &amp; Tarm</a></li>
627            
628            
629            
630                            <li><a href="/tillskott/multivitamin" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Multivitamin</a></li>
631            
632            
633            
634                            <li><a href="/tillskott/omega" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Omega</a></li>
635            
636            
637            
638                            <li><a href="/tillskott/allmanhalsa" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Allmänhälsa</a></li>
639            
640            
641            
642                            <li><a href="/tillskott/valp-tillskott" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Valp</a></li>
643            
644            
645            
646                            <li><a href="/tillskott/lugnande" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Lugnande</a></li>
647            
648                      </ul>
649                    </li>
650            
651            
652            
653                    <li x-data="dropdown" @mouseover="open = navbarLevelsDepth >= 1 && true" @mouseleave="open = false" class=" mx-2 lg:mx-4 group">
654                      <div class="relative flex items-center justify-center">
655                        <a href="/tillbehor" id="menu-item-107"
656                          class="flex py-2 px-1 transition ease-in-out duration-default  hover:opacity-70 ">
657                          <span>Tillbehör</span>
658                        </a>
659                        <svg x-show="navbarLevelsDepth >= 1"  xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
660                            stroke-width="1.5" stroke="currentColor"
661                            class="w-4 h-4 cursor-pointer transform duration-default rotate-90 " :class="{'rotate-0' : open}">
662                            <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
663                        </svg>
664                      </div>
665            
666                      <!-- First drop down -->
667                      <ul x-cloak
668                        x-show="open"
669                        "
670                        x-transition:enter='transition ease-out transform'
671                        x-transition:enter-start='opacity-0 -translate-y-4'
672                        x-transition:enter-end='opacity-100 translate-y-0'
673                        x-transition:leave='transition ease-in transform'
674                        x-transition:leave-start='opacity-100 translate-y-0'
675                        x-transition:leave-end='opacity-0 -translate-y-4'
676                        class="absolute z-50 w-full max-w-xs px-6 py-2 bg-header shadow-md roundednone duration-default" aria-labelledby="menu-item-107">
677            
678            
679                            <li><a href="/tillbehor/hud-palsvard" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Hud &amp; Pälsvård</a></li>
680            
681            
682            
683                            <li><a href="/tillbehor/bajspasar" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Bajspåsar</a></li>
684            
685            
686            
687                            <li><a href="/tillbehor/munhygien" class="block py-3 transition ease-in-out duration-default hover:opacity-70 ">Munhygien</a></li>
688            
689                      </ul>
690                    </li>
691            
692            
693                    <li class=" mx-2 lg:mx-4">
694                      <a class="block py-2 px-1 transition ease-in-out duration-default  hover:opacity-70 " href="/erbjudanden"
695                        title="Erbjudanden">
696                        <span>Erbjudanden</span>
697                      </a>
698                    </li>
699            
700            
701            
702                    <li class=" mx-2 lg:mx-4">
703                      <a class="block py-2 px-1 transition ease-in-out duration-default  hover:opacity-70 " href="/sidor/information"
704                        title="Information">
705                        <span>Information</span>
706                      </a>
707                    </li>
708            
709            
710            
711                    <li class=" mx-2 lg:mx-4">
712                      <a class="block py-2 px-1 transition ease-in-out duration-default  hover:opacity-70 " href="/blogg"
713                        title="Blogg">
714                        <span>Blogg</span>
715                      </a>
716                    </li>
717            
718            
719            
720                    <li class=" mx-2 lg:mx-4">
721                      <a class="block py-2 px-1 transition ease-in-out duration-default  hover:opacity-70 " href="/presentkort"
722                        title="Presentkort">
723                        <span>Presentkort</span>
724                      </a>
725                    </li>
726            
727            
728              
729              </ul>
730            </div>
731          </div>
732        </nav>
733      
734        <!-- Menu -->
735        <div
736          x-cloak
737          @keydown.escape="navMenuOpen = false"
738          x-show="navMenuOpen"
739          class="navbar-menu fixed top-0 left-0 bottom-0 w-full sm:max-w-sm z-50">
740          
741          <!-- Overlay -->
742          <div
743            x-show='navMenuOpen'
744            @click='navMenuOpen = false'
745            x-transition:enter='transition ease-out duration-default transform'
746            x-transition:enter-start='opacity-0'
747            x-transition:enter-end='opacity-100'
748            x-transition:leave='transition ease-in duration-default transform'
749            x-transition:leave-start='opacity-100'
750            x-transition:leave-end='opacity-0'
751            class='fixed inset-0 transition-opacity bg-overlay bg-opacity-40'
752            aria-hidden='true'
753          ></div>
754          
755          <!-- Menu -->
756          <nav
757            x-show='navMenuOpen'
758            @keydown.escape="navMenuOpen = false"
759            class="relative flex flex-col h-full bg-body"
760            x-transition:enter='transition ease-out duration-default transform'
761            x-transition:enter-start='opacity-0 -translate-x-10 sm:-translate-x-10'
762            x-transition:enter-end='opacity-100 translate-x-0'
763            x-transition:leave='transition ease-in duration-default transform'
764            x-transition:leave-start='opacity-100 translate-x-0'
765            x-transition:leave-end='opacity-0 -translate-x-10 sm:-translate-x-10'
766          >
767            <div class="flex items-center justify-between mb-12 p-6">
768        
769              <!-- Logo -->
770              <a class="items-center" href="https://vecanis.se">
771                
772                <img alt="Vecanis" class="img-fluid img-logo w-32"
773                  ix-src="https://cdn.quickbutik.com/images/2666L/templates/lua/assets/logo.png?s=96971&auto=format">
774              </a>
775        
776              <!-- Close button -->
777              <button @click="navMenuOpen = false" class="inline-block navbar-close focus:outline-none" type="button"
778                aria-label="Close">
779                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
780                  stroke="currentColor" class="w-6 h-6">
781                  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path>
782                </svg></button>
783            </div>
784        
785            <!-- Menu -->
786            <div class="px-6 overflow-y-auto">
787        
788              <ul>
789        
790                <li x-data="{expanded:false}" class="active">
791                  <div class="flex">
792                    <a href="/hundmat"
793                      class="flex flex-1 py-3 font-bold hover:text-gray-900 active">
794                      <span id="navMenuItem8">Hundmat</span>
795                    </a>
796                    <div class="flex h-full flex-col py-3">
797                      <svg @click="expanded = !expanded" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
798                        stroke="currentColor" class="w-6 h-6 cursor-pointer transform duration-default" :class="{'rotate-90' : expanded}">
799                        <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
800                      </svg>
801                    </div>
802                  </div>
803                  <ul x-show="expanded"
804                    x-transition:enter='transition ease-out duration-default transform'
805                    x-transition:enter-start='opacity-0 -translate-y-4'
806                    x-transition:enter-end='opacity-100 translate-y-0'
807                    x-transition:leave='transition ease-in duration-default transform'
808                    x-transition:leave-start='opacity-100 translate-y-0'
809                    x-transition:leave-end='opacity-0 -translate-y-4'
810                    class="pl-4" aria-labelledby="navMenuItem8">
811        
812        
813                      <li><a href="/hundmat/topping" class="block py-3 ">Topping</a></li>
814        
815        
816        
817                      <li><a href="/hundmat/blotmat" class="block py-3 ">Blötmat</a></li>
818        
819        
820        
821                      <li><a href="/hundmat/torrfoder" class="block py-3 ">Torrfoder</a></li>
822        
823        
824        
825                      <li><a href="/hundmat/allergivanlig-hundmat" class="block py-3 ">Allergi</a></li>
826        
827        
828        
829                      <li><a href="/hundmat/glutenfritt" class="block py-3 ">Glutenfritt</a></li>
830        
831        
832        
833                      <li><a href="/hundmat/fettsnalt" class="block py-3 ">Fettsnålt</a></li>
834        
835        
836        
837                      <li><a href="/hundmat/spannmalsfritt" class="block py-3 ">Spannmålsfritt</a></li>
838        
839        
840        
841                      <li><a href="/hundmat/senior" class="block py-3 ">Senior</a></li>
842        
843        
844        
845                      <li><a href="/hundmat/valp" class="block py-3 ">Valp</a></li>
846        
847        
848        
849                      <li><a href="/hundmat/mini" class="block py-3 ">Mini</a></li>
850        
851        
852                  </ul>
853                </li>
854              </ul>
855        
856        
857              <ul>
858        
859                <li x-data="{expanded:false}" class="">
860                  <div class="flex">
861                    <a href="/hundgodis"
862                      class="flex flex-1 py-3 font-bold hover:text-gray-900 ">
863                      <span id="navMenuItem146">Hundgodis</span>
864                    </a>
865                    <div class="flex h-full flex-col py-3">
866                      <svg @click="expanded = !expanded" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
867                        stroke="currentColor" class="w-6 h-6 cursor-pointer transform duration-default" :class="{'rotate-90' : expanded}">
868                        <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
869                      </svg>
870                    </div>
871                  </div>
872                  <ul x-show="expanded"
873                    x-transition:enter='transition ease-out duration-default transform'
874                    x-transition:enter-start='opacity-0 -translate-y-4'
875                    x-transition:enter-end='opacity-100 translate-y-0'
876                    x-transition:leave='transition ease-in duration-default transform'
877                    x-transition:leave-start='opacity-100 translate-y-0'
878                    x-transition:leave-end='opacity-0 -translate-y-4'
879                    class="pl-4" aria-labelledby="navMenuItem146">
880        
881        
882                      <li><a href="/hundgodis/tuggben" class="block py-3 ">Tuggben</a></li>
883        
884        
885        
886                      <li><a href="/hundgodis/beloningsgodis" class="block py-3 ">Belöningsgodis</a></li>
887        
888        
889        
890                      <li><a href="/hundgodis/allergi-hundgodis" class="block py-3 ">Allergi</a></li>
891        
892        
893        
894                      <li><a href="/hundgodis/glutenfritt-hundgodis" class="block py-3 ">Glutenfritt</a></li>
895        
896        
897        
898                      <li><a href="/hundgodis/fettsnalt-hundgodis" class="block py-3 ">Fettsnålt</a></li>
899        
900        
901        
902                      <li><a href="/hundgodis/spannmalsfritt-hundgodis" class="block py-3 ">Spannmålsfritt</a></li>
903        
904        
905        
906                      <li><a href="/hundgodis/dentalgodis" class="block py-3 ">Dentalgodis</a></li>
907        
908        
909        
910                      <li><a href="/hundgodis/seniorhundgodis" class="block py-3 ">Senior</a></li>
911        
912        
913        
914                      <li><a href="/hundgodis/valp-hundgodis" class="block py-3 ">Valp</a></li>
915        
916        
917        
918                      <li><a href="/hundgodis/mini" class="block py-3 ">Mini</a></li>
919        
920        
921        
922                      <li><a href="/hundgodis/jumbo" class="block py-3 ">Jumbo</a></li>
923        
924        
925        
926                      <li><a href="/ovrigt" class="block py-3 ">Övrigt</a></li>
927        
928        
929                  </ul>
930                </li>
931              </ul>
932        
933        
934              <ul>
935        
936                <li x-data="{expanded:false}" class="">
937                  <div class="flex">
938                    <a href="/tillskott"
939                      class="flex flex-1 py-3 font-bold hover:text-gray-900 ">
940                      <span id="navMenuItem105">Tillskott</span>
941                    </a>
942                    <div class="flex h-full flex-col py-3">
943                      <svg @click="expanded = !expanded" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
944                        stroke="currentColor" class="w-6 h-6 cursor-pointer transform duration-default" :class="{'rotate-90' : expanded}">
945                        <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
946                      </svg>
947                    </div>
948                  </div>
949                  <ul x-show="expanded"
950                    x-transition:enter='transition ease-out duration-default transform'
951                    x-transition:enter-start='opacity-0 -translate-y-4'
952                    x-transition:enter-end='opacity-100 translate-y-0'
953                    x-transition:leave='transition ease-in duration-default transform'
954                    x-transition:leave-start='opacity-100 translate-y-0'
955                    x-transition:leave-end='opacity-0 -translate-y-4'
956                    class="pl-4" aria-labelledby="navMenuItem105">
957        
958        
959                      <li><a href="/tillskott/26-munhygien" class="block py-3 ">Munhygien</a></li>
960        
961        
962        
963                      <li><a href="/tillskott/pals-hud" class="block py-3 ">Päls &amp; Hud</a></li>
964        
965        
966        
967                      <li><a href="/tillskott/leder-rorlighet" class="block py-3 ">Leder &amp; Rörlighet</a></li>
968        
969        
970        
971                      <li><a href="/tillskott/mage" class="block py-3 ">Mage &amp; Tarm</a></li>
972        
973        
974        
975                      <li><a href="/tillskott/multivitamin" class="block py-3 ">Multivitamin</a></li>
976        
977        
978        
979                      <li><a href="/tillskott/omega" class="block py-3 ">Omega</a></li>
980        
981        
982        
983                      <li><a href="/tillskott/allmanhalsa" class="block py-3 ">Allmänhälsa</a></li>
984        
985        
986        
987                      <li><a href="/tillskott/valp-tillskott" class="block py-3 ">Valp</a></li>
988        
989        
990        
991                      <li><a href="/tillskott/lugnande" class="block py-3 ">Lugnande</a></li>
992        
993        
994                  </ul>
995                </li>
996              </ul>
997        
998        
999              <ul>
1000        
1001                <li x-data="{expanded:false}" class="">
1002                  <div class="flex">
1003                    <a href="/tillbehor"
1004                      class="flex flex-1 py-3 font-bold hover:text-gray-900 ">
1005                      <span id="navMenuItem107">Tillbehör</span>
1006                    </a>
1007                    <div class="flex h-full flex-col py-3">
1008                      <svg @click="expanded = !expanded" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
1009                        stroke="currentColor" class="w-6 h-6 cursor-pointer transform duration-default" :class="{'rotate-90' : expanded}">
1010                        <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
1011                      </svg>
1012                    </div>
1013                  </div>
1014                  <ul x-show="expanded"
1015                    x-transition:enter='transition ease-out duration-default transform'
1016                    x-transition:enter-start='opacity-0 -translate-y-4'
1017                    x-transition:enter-end='opacity-100 translate-y-0'
1018                    x-transition:leave='transition ease-in duration-default transform'
1019                    x-transition:leave-start='opacity-100 translate-y-0'
1020                    x-transition:leave-end='opacity-0 -translate-y-4'
1021                    class="pl-4" aria-labelledby="navMenuItem107">
1022        
1023        
1024                      <li><a href="/tillbehor/hud-palsvard" class="block py-3 ">Hud &amp; Pälsvård</a></li>
1025        
1026        
1027        
1028                      <li><a href="/tillbehor/bajspasar" class="block py-3 ">Bajspåsar</a></li>
1029        
1030        
1031        
1032                      <li><a href="/tillbehor/munhygien" class="block py-3 ">Munhygien</a></li>
1033        
1034        
1035                  </ul>
1036                </li>
1037              </ul>
1038        
1039        
1040              <ul>
1041                  <li class="">
1042                    <a class="block py-3 font-bold hover:text-gray-900 " href="/erbjudanden"
1043                      title="Erbjudanden">
1044                      <span>Erbjudanden</span>
1045                    </a>
1046                  </li>
1047        
1048              </ul>
1049        
1050        
1051              <ul>
1052                  <li class="">
1053                    <a class="block py-3 font-bold hover:text-gray-900 " href="/sidor/information"
1054                      title="Information">
1055                      <span>Information</span>
1056                    </a>
1057                  </li>
1058        
1059              </ul>
1060        
1061        
1062              <ul>
1063                  <li class="">
1064                    <a class="block py-3 font-bold hover:text-gray-900 " href="/blogg"
1065                      title="Blogg">
1066                      <span>Blogg</span>
1067                    </a>
1068                  </li>
1069        
1070              </ul>
1071        
1072        
1073              <ul>
1074                  <li class="">
1075                    <a class="block py-3 font-bold hover:text-gray-900 " href="/presentkort"
1076                      title="Presentkort">
1077                      <span>Presentkort</span>
1078                    </a>
1079                  </li>
1080        
1081              </ul>
1082        
1083            </div>
1084        
1085            <div class="p-6 mt-auto">
1086        
1087              <!-- Login -->
1088              <a class="block px-6 py-3 text-center button-rounding bg-foreground text-white font-bold bg-fore hover:opacity-90"
1089                href="/customer/login">Logga in</a>
1090        
1091            </div>
1092          </nav>
1093        </div>        <!-- Settings modal -->
1094        <template x-teleport='body'>
1095          <div
1096            x-data
1097            x-cloak
1098            x-show='$store.settings.settingsOpen'
1099            @keydown.escape='$store.settings.hideSettings()'
1100            class='fixed inset-0 z-50 overflow-y-auto'
1101            aria-labelledby='modal-title'
1102            role='dialog'
1103            aria-modal='true'
1104          >
1105            <div class='flex items-end justify-center min-h-screen text-center md:items-center sm:block'>
1106              
1107              <!-- Overlay -->
1108              <div
1109                @click='$store.settings.hideSettings()'
1110                x-show='$store.settings.settingsOpen'
1111                x-transition:enter='transition ease-out duration-default transform'
1112                x-transition:enter-start='opacity-0'
1113                x-transition:enter-end='opacity-100'
1114                x-transition:leave='transition ease-in duration-default transform'
1115                x-transition:leave-start='opacity-100'
1116                x-transition:leave-end='opacity-0'
1117                class='fixed inset-0 transition-opacity bg-overlay bg-opacity-40'
1118                aria-hidden='true'></div>
1119        
1120              <!-- Modal -->
1121              <div x-show='$store.settings.settingsOpen'
1122                x-transition:enter='transition ease-out duration-default transform'
1123                x-transition:enter-start='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
1124                x-transition:enter-end='opacity-100 translate-y-0 sm:scale-100'
1125                x-transition:leave='transition ease-in duration-default transform'
1126                x-transition:leave-start='opacity-100 translate-y-0 sm:scale-100'
1127                x-transition:leave-end='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
1128                class='flex flex-col sm:inline-block w-full max-w-xl fixed top-0 bottom-0 sm:top-auto sm:bottom-auto sm:static sm:my-20 overflow-hidden text-left transition-all transform bg-body roundednone shadow-xl 2xl:max-w-2xl'>
1129        
1130                <!-- Header -->  
1131                <header class='flex items-center justify-between p-6 mb-2'>
1132        
1133                  <!-- Title -->
1134                  <span class='text-3xl font-bold font-heading'>Inställningar</span>
1135        
1136                  <!-- Close button -->
1137                  <button @click='$store.settings.hideSettings()' class='text-gray-600 focus:outline-none hover:text-gray-700'>
1138                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
1139                      <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path>
1140                    </svg>
1141                  </button>
1142                </header>
1143        
1144                <div class="px-6 pb-6 mb-6 h-full overflow-y-auto">
1145        
1146                  <!-- Languages -->
1147                  <div class="mb-4">
1148                    <h4 class="text-lg font-heading font-bold">Språk</h4>
1149                    <div>
1150                      <a href="https://vecanis.se/hundmat" class="flex mt-2 px-4 py-2 bg-primary text-white button-rounding hover:bg-gray-200 duration-default transition-shadow">
1151                        <img loading="lazy" class="w-auto" src="/assets/shopassets/flags/24/sv.png" alt="sv Flag">
1152                        <span class="pl-2">Svenska</span>
1153                      </a>
1154                      <a href="https://vecanis.se/en/hundmat" class="flex mt-2 px-4 py-2 bg-gray-100 button-rounding hover:bg-gray-200 duration-default transition-shadow">
1155                        <img loading="lazy" class="w-auto" src="/assets/shopassets/flags/24/en.png" alt="en Flag">
1156                        <span class="pl-2">English</span>
1157                      </a>
1158                    </div>
1159                  </div>
1160        
1161                  <!-- Tax -->
1162        
1163                  <!-- Currencies-->
1164                  <div>
1165                    <h4 class="text-lg font-heading font-bold">Valuta</h4>
1166                    <div class="flex flex-wrap">
1167                      <a href="https://vecanis.se/hundmat?currency=DKK"
1168                        class="dropdown-item mt-2 mr-2 px-4 py-2 bg-gray-100 button-rounding hover:bg-gray-200 duration-default transition-colors">DKK</a>
1169                      <a href="https://vecanis.se/hundmat?currency=EUR"
1170                        class="dropdown-item mt-2 mr-2 px-4 py-2 bg-gray-100 button-rounding hover:bg-gray-200 duration-default transition-colors">EUR</a>
1171                      <a href="https://vecanis.se/hundmat?currency=SEK"
1172                        class="dropdown-item mt-2 mr-2 px-4 py-2 bg-primary text-white button-rounding hover:bg-gray-200 duration-default transition-colors">SEK</a>
1173                    </div>
1174                  </div>
1175                
1176                </div>
1177              </div>
1178            </div>
1179          </div>
1180        </template>        <!-- Search modal -->
1181        <template x-teleport='body'>
1182          <div
1183            x-data='appSearchModal'
1184            x-cloak
1185            x-show='$store.search.searchOpen'
1186            @keydown.escape='$store.search.hideSearch()'
1187            class='fixed inset-0 z-50 overflow-y-auto'
1188            aria-labelledby='modal-title'
1189            role='dialog'
1190            aria-modal='true'
1191          >
1192            <div class='flex items-end justify-center min-h-screen px-4 text-center md:items-center sm:block sm:p-0'>
1193        
1194              <!-- Overlay -->
1195              <div
1196                @click='$store.search.hideSearch()'
1197                x-show='$store.search.searchOpen'
1198                x-transition:enter='transition ease-out duration-default transform'
1199                x-transition:enter-start='opacity-0'
1200                x-transition:enter-end='opacity-100'
1201                x-transition:leave='transition ease-in duration-default transform'
1202                x-transition:leave-start='opacity-100'
1203                x-transition:leave-end='opacity-0'
1204                class='fixed inset-0 transition-opacity bg-overlay bg-opacity-40'
1205                aria-hidden='true'
1206              ></div>
1207        
1208              <!-- Modal -->
1209              <div
1210                modal="search"
1211                x-show='$store.search.searchOpen'
1212                x-transition:enter='transition ease-out duration-default transform'
1213                x-transition:enter-start='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
1214                x-transition:enter-end='opacity-100 translate-y-0 sm:scale-100'
1215                x-transition:leave='transition ease-in duration-default transform'
1216                x-transition:leave-start='opacity-100 translate-y-0 sm:scale-100'
1217                x-transition:leave-end='opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95'
1218                class='flex flex-col h-screen sm:inline-block w-full h-max-screen sm:max-w-xl 2xl:max-w-2xl fixed top-0 bottom-0 sm:top-auto sm:bottom-auto sm:static sm:my-20 overflow-hidden text-left transition-all transform'
1219                style='@media (min-width: 576px) { max-height: calc(80vh) }'
1220              >
1221                <div class="pointer-events-auto relative flex w-full flex-col max-h-full overflow-hidden border-none bg-body shadow-xl">
1222        
1223                  <!-- Header -->
1224                  <header class='flex flex-col flex-shrink-0 p-6'>
1225        
1226                    <div class="flex items-center justify-between pb-6">
1227        
1228                      <!-- Title -->
1229                      <span class='text-3xl font-bold font-heading'>Sök</span>
1230        
1231                      <!-- Close button -->
1232                      <button
1233                        @click='$store.search.hideSearch()'
1234                        class='text-gray-600 focus:outline-none hover:text-gray-700'>
1235                        <svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='currentColor' class='w-6 h-6'>
1236                          <path stroke-linecap='round' stroke-linejoin='round' d='M6 18L18 6M6 6l12 12'></path>
1237                        </svg>
1238                      </button>
1239                    </div>
1240        
1241                    <form
1242                      action='/shop/search'
1243                      method='get' class='flex'>
1244                      <div class='flex w-full relative'>
1245                        <div class="flex h-full flex-col justify-center absolute top-0 left-0 pl-2">
1246                          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 opacity-40">
1247                            <path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
1248                          </svg>
1249                        </div>
1250                        <input
1251                          type='search'
1252                          name='s'
1253                          x-model='searchTerm'
1254                          id='searchInput'
1255                          @input.debounce.500ms="search"
1256                          placeholder='Sök produkter'
1257                          aria-label='Search'
1258                          aria-describedby='button-search-desktop'
1259                          class='flex flex-1 py-3 pr-3 pl-14 input-bg input-text input-border border input-rounding focus:outline-none'
1260                        >
1261                      </div>
1262                    </form>
1263                  </header>
1264        
1265                  <!-- No results found -->
1266                  <div x-show='searchTerm && noResults' class="px-6 mb-8">
1267                    <p class="mt-8 text-center">
1268                      Inga sökresultat hittades
1269                    </p>
1270                  </div>
1271        
1272                  <!-- Search results -->
1273                  <div x-show='!noResults && searchTerm != ""' class="relative overflow-y-auto pb-6">
1274                    <template x-for='result in results'>
1275                      <a :href='result.product.url'>
1276                        <div class="flex py-2 px-6 border-b border-gray-100  hover:bg-gray-100">
1277                          <div class="flex flex-0 w-16 h-16 mr-6">
1278                            <img :src="result.product.firstimage + '?w=100&h=100'" ix-sizes="auto" :alt="result.product.title" class="w-full object-contain object-center">
1279                          </div>
1280                          <div class="flex flex-1 flex-col justify-center">
1281                            <span x-html='highlightTitle(result.product.title)' class=""></span>
1282                          </div>
1283                          <div class="flex flex-col justify-center">
1284                            <span x-text='result.product.price' class=""></span>
1285                          </div>
1286                        </div>
1287                      </a>
1288                    </template>
1289                  </div>
1290              
1291                </div>
1292        
1293              </div>
1294            </div>
1295          </div>
1296        </template>
1297        
1298      </section>      
1299
1300
1301      <!-- Breadcrumbs -->
1302      <section data-qb-area="general" data-qb-setting="show_breadcrumbs" class="py-4">
1303        <div class="container mx-auto px-4">
1304          <ul class="flex flex-wrap justify-center">
1305            
1306            <li class="flex items-center mr-3">
1307              
1308              
1309              <a class="inline-block mr-3 text-sm opacity-70" href="/">Hem</a>
1310              <span class="inline-block opacity-40">
1311                <svg class="h-2 w-2" viewbox="0 0 6 11" fill="none" xmlns="http://www.w3.org/2000/svg">
1312                  <path d="M0.131073 0.755835C-0.0436907 0.581885 -0.0436907 0.302414 0.131073 0.129744C0.305837 -0.0429273 0.588197 -0.
13120435662 0.76296 0.129744L5.86893 5.18707C6.04369 5.35975 6.04369 5.63922 5.86893 5.81317L0.762959 10.8705C0.588196 11.0432 0.305836 11.0432 0.131072 10.8705C-0.0436916 10.6972 -0.0436916 10.4171 0.131072 10.2444L4.78774 5.4998L0.131073 0.755835Z" fill="#151515"></path>
1313                </svg>
1314              </span>
1315
1316            </li>
1317            <li class="flex items-center mr-3">
1318              
1319              <p class="inline-block text-sm opacity-70" href="#">Hundmat</p>
1320              
1321
1322            </li>
1323
1324          </ul>
1325        </div>
1326      </section>
1327
1328      <section class="py-16" data-qb-category-id="2">
1329        <div class="container mx-auto px-4">
1330      
1331          <!-- Title -->
1332          <h1 class="mb-8 font-heading text-center text-4xl">
1333            Hundmat
1334          </h1>
1335      
1336          <!-- Description -->
1337          <div class="mx-auto max-w-3xl text-lg mb-16 applyTailwindClasses">
1338            <h1 style="text-align: center;"><span style="font-size: 28px;">Växtbaserad hundmat - Ett sunt och klimatsmart val</span></h1><p style="text-align: justify;">Utforska vårt utbud av växtbaserad klimatvänlig hundmat och ta steget mot en hållbarare och miljövänligare livsstil för din fyrbenta vän. Vår noggrant utvalda hundmat är inte bara snäll mot miljön utan erbjuder också en näringsrik och balanserad kost för din hund. Genom att välja växtbaserade alternativ kan du minska din husdjurs klimatpåverkan och främja en mer hållbar framtid. Ge din hund det bästa samtidigt som du tar ett steg mot att minska den ekologiska fotavtrycket. Upptäck våra klimatvänliga alternativ och skäm bort din hund med omtanke om både dess hälsa och vår planet.</p>
1339          </div>
1340      
1341          <!-- Categories -->
1342      
1343          <hr class="border-foreground opacity-20 mt-16">
1344      
1345          <!-- Products filter and sort -->
1346          <div class="w-full my-6 flex justify-between">
1347            <div class="gap-2 flex justify-end" data-qb-selector="filter-container"></div>
1348            <div class="flex items-start">
1349              <div class="flex justify-end" data-qb-selector="sort-container"></div>
1350            </div>
1351          </div>
1352      
1353          <div class="flex flex-wrap mt-6">
1354            <div class="w-full">
1355              <div id="productList" class="grid grid-cols-2 sm:grid-cols-2 lg:grid-cols-4 gap-default">
1356                
1357                <!-- Products -->
1358      
1359
1360
1361
1362                  
1363
1364                  
1365
1366
1367
1368                  
1369
1370                  
1371                  <div x-data="{ layoutSingle: '4' == '1' }"
1372                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
1373                  
1374                    data-qb-selector="product-item"
1375                    data-product-id="989"
1376                  
1377                    data-pid="989"
1378                    data-s-title="Utgånget! VEGDOG Vuxen Linser &amp; Hirs 800g"
1379                    data-s-price="59.00"
1380                    data-price="59.00"
1381                  >
1382                  
1383                    <!-- Image -->
1384                    <a class="block relative w-full " href="/hundmat/22-kopia-vegdog-vuxen-linser-hirs-400g800g">
1385                      <div class="relative group overflow-hidden rounded-tnone">
1386                        <img loading="lazy" 
1387                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
1388                          ix-src="https://cdn.quickbutik.com/images/2666L/products/178713818815f0eec1656a138de4e49.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Utgånget! VEGDOG Vuxen Linser &amp; Hirs 800g">
1389                        
1390                        <img loading="lazy"
1391                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
1392                          ix-src="https://cdn.quickbutik.com/images/2666L/products/17871381893764a81b656a138de15dc.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Utgånget! VEGDOG Vuxen Linser &amp; Hirs 800g">
1393                        
1394                      </div>
1395                    </a>
1396                  
1397                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
1398                      <a class="block px-6 mb-4" href="/hundmat/22-kopia-vegdog-vuxen-linser-hirs-400g800g">
1399                        
1400                        <!-- Title -->
1401                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
1402                          Utgånget! VEGDOG Vuxen Linser &amp; Hirs 800g
1403                        </h3>
1404                  
1405                        <!-- Reviews container -->
1406                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
1407                  
1408                      </a>
1409                  
1410                      <!-- Description -->
1411                      <template x-if="layoutSingle">
1412                        
1413                      </template>
1414                  
1415                      <div>
1416                  
1417                        <!-- Price -->
1418                        <div class="px-6">
1419                  
1420                          <!-- Current price -->
1421                          <span>
1422                            <span class="text-xl font-bold text-accent">59 SEK</span>
1423                          </span>
1424                          
1425                          <!-- Original price -->
1426                  
1427                        </div>
1428                  
1429                        <!-- Add to basket -->
1430                        <div class="flex justify-between gap-2 px-6 mt-2">
1431                  
1432                            <!-- Buy button -->
1433                              
1434                              <!-- No options -->
1435                  
1436                              <!-- Has options -->
1437                              <a href="/hundmat/22-kopia-vegdog-vuxen-linser-hirs-400g800g" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
1438                  
1439                                <!-- Icon (arrow) -->
1440                  
1441                                <!-- Text -->
1442                                Läs mer
1443                  
1444                              </a>
1445                  
1446                              <!-- Sold out -->
1447                            
1448                  
1449                  
1450                            
1451                            <!-- Link to product when no buy button -->
1452                  
1453                            <!-- Wishlist -->
1454                            <div data-qb-selector="product-item-wishlist" data-product-id="989" class="flex items-center"></div>
1455                  
1456                        </div>
1457                        
1458                        <!-- Stock status -->
1459                        <div class="text-sm px-6 pt-4">
1460                  
1461                          <!-- Sold out -->
1462                  
1463                          <!-- Not sold out -->
1464                          <span data-in-stock-text="I lager"
1465                            data-out-of-stock-text=""
1466                            data-combination-not-exist-text="Variant finns ej">
1467                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
1468                          </span>
1469                  
1470                        </div>
1471                      
1472                      </div>
1473                    </div>
1474                  </div>
1475      
1476      
1477
1478
1479
1480                  
1481
1482                  
1483
1484
1485
1486                  
1487
1488                  
1489                  <div x-data="{ layoutSingle: '4' == '1' }"
1490                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
1491                  
1492                    data-qb-selector="product-item"
1493                    data-product-id="984"
1494                  
1495                    data-pid="984"
1496                    data-s-title="Omni Sensitive Adult"
1497                    data-s-price="249.00"
1498                    data-price="249.00"
1499                  >
1500                  
1501                    <!-- Image -->
1502                    <a class="block relative w-full " href="/hundmat/omni-sensitive-adult-dry-food-2kg6kg10kg">
1503                      <div class="relative group overflow-hidden rounded-tnone">
1504                        <img loading="lazy" 
1505                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
1506                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6a4621605e1e0.webp" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Sensitive Adult">
1507                        
1508                        <img loading="lazy"
1509                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
1510                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6a462165757b9.webp" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Sensitive Adult">
1511                        
1512                      </div>
1513                    </a>
1514                  
1515                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
1516                      <a class="block px-6 mb-4" href="/hundmat/omni-sensitive-adult-
1516dry-food-2kg6kg10kg">
1517                        
1518                        <!-- Title -->
1519                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
1520                          Omni Sensitive Adult
1521                        </h3>
1522                  
1523                        <!-- Reviews container -->
1524                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
1525                  
1526                      </a>
1527                  
1528                      <!-- Description -->
1529                      <template x-if="layoutSingle">
1530                        
1531                      </template>
1532                  
1533                      <div>
1534                  
1535                        <!-- Price -->
1536                        <div class="px-6">
1537                  
1538                          <!-- Current price -->
1539                          <span>
1540                            <span class="text-xl font-bold text-accent">249 SEK</span>
1541                          </span>
1542                          
1543                          <!-- Original price -->
1544                  
1545                        </div>
1546                  
1547                        <!-- Add to basket -->
1548                        <div class="flex justify-between gap-2 px-6 mt-2">
1549                  
1550                            <!-- Buy button -->
1551                              
1552                              <!-- No options -->
1553                  
1554                              <!-- Has options -->
1555                              <a href="/hundmat/omni-sensitive-adult-dry-food-2kg6kg10kg" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
1556                  
1557                                <!-- Icon (arrow) -->
1558                  
1559                                <!-- Text -->
1560                                Läs mer
1561                  
1562                              </a>
1563                  
1564                              <!-- Sold out -->
1565                            
1566                  
1567                  
1568                            
1569                            <!-- Link to product when no buy button -->
1570                  
1571                            <!-- Wishlist -->
1572                            <div data-qb-selector="product-item-wishlist" data-product-id="984" class="flex items-center"></div>
1573                  
1574                        </div>
1575                        
1576                        <!-- Stock status -->
1577                        <div class="text-sm px-6 pt-4">
1578                  
1579                          <!-- Sold out -->
1580                  
1581                          <!-- Not sold out -->
1582                          <span data-in-stock-text="I lager"
1583                            data-out-of-stock-text=""
1584                            data-combination-not-exist-text="Variant finns ej">
1585                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
1586                          </span>
1587                  
1588                        </div>
1589                      
1590                      </div>
1591                    </div>
1592                  </div>
1593      
1594      
1595
1596
1597
1598                  
1599
1600                  
1601
1602
1603
1604                  
1605
1606                  
1607                  <div x-data="{ layoutSingle: '4' == '1' }"
1608                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
1609                  
1610                    data-qb-selector="product-item"
1611                    data-product-id="982"
1612                  
1613                    data-pid="982"
1614                    data-s-title="Utgånget! Omni Toppers 150g"
1615                    data-s-price="15.00"
1616                    data-price="15.00"
1617                  >
1618                  
1619                    <!-- Image -->
1620                    <a class="block relative w-full " href="/hundmat/kort-datum-omni-toppers-150g">
1621                      <div class="relative group overflow-hidden rounded-tnone">
1622                        <img loading="lazy" 
1623                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
1624                          ix-src="https://cdn.quickbutik.com/images/2666L/products/17821923467a6e233866e91cfeb969e.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Utgånget! Omni Toppers 150g">
1625                        
1626                        <img loading="lazy"
1627                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
1628                          ix-src="https://cdn.quickbutik.com/images/2666L/products/1782192346e904e2e866e91d1ec37b7.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Utgånget! Omni Toppers 150g">
1629                        
1630                      </div>
1631                    </a>
1632                  
1633                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
1634                      <a class="block px-6 mb-4" href="/hundmat/kort-datum-omni-toppers-150g">
1635                        
1636                        <!-- Title -->
1637                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
1638                          Utgånget! Omni Toppers 150g
1639                        </h3>
1640                  
1641                        <!-- Reviews container -->
1642                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
1643                  
1644                      </a>
1645                  
1646                      <!-- Description -->
1647                      <template x-if="layoutSingle">
1648                        
1649                      </template>
1650                  
1651                      <div>
1652                  
1653                        <!-- Price -->
1654                        <div class="px-6">
1655                  
1656                          <!-- Current price -->
1657                          <span>
1658                            <span class="text-xl font-bold text-discounted-price">15 SEK</span>
1659                          </span>
1660                          
1661                          <!-- Original price -->
1662                          <span class="opacity-40 line-through mr-2">
1663                            45 SEK
1664                          </span>
1665                  
1666                        </div>
1667                  
1668                        <!-- Add to basket -->
1669                        <div class="flex justify-between gap-2 px-6 mt-2">
1670                  
1671                            <!-- Buy button -->
1672                              
1673                              <!-- No options -->
1674                  
1675                              <!-- Has options -->
1676                              <a href="/hundmat/kort-datum-omni-toppers-150g" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
1677                  
1678                                <!-- Icon (arrow) -->
1679                  
1680                                <!-- Text -->
1681                                Läs mer
1682                  
1683                              </a>
1684                  
1685                              <!-- Sold out -->
1686                            
1687                  
1688                  
1689                            
1690                            <!-- Link to product when no buy button -->
1691                  
1692                            <!-- Wishlist -->
1693                            <div data-qb-selector="product-item-wishlist" data-product-id="982" class="flex items-center"></div>
1694                  
1695                        </div>
1696                        
1697                        <!-- Stock status -->
1698                        <div class="text-sm px-6 pt-4">
1699                  
1700                          <!-- Sold out -->
1701                  
1702                          <!-- Not sold out -->
1703                          <span data-in-stock-text="I lager"
1704                            data-out-of-stock-text=""
1705                            data-combination-not-exist-text="Variant finns ej">
1706                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
1707                          </span>
1708                  
1709                        </div>
1710                      
1711                      </div>
1712                    </div>
1713                  </div>
1714      
1715      
1716
1717
1718
1719                  
1720
1721                  
1722
1723
1724
1725                  
1726
1727                  
1728                  <div x-data="{ layoutSingle: '4' == '1' }"
1729                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
1730                  
1731                    data-qb-selector="product-item"
1732                    data-product-id="979"
1733                  
1734                    data-pid="979"
1735                    data-s-title="Skadad förpackning! VEGDOG Green Crunch 10kg"
1736                    data-s-price="599.00"
1737                    data-price="599.00"
1738                  >
1739                  
1740                    <!-- Image -->
1741                    <a class="block relative w-full " href="/hundmat/skadad-forpackning-vegdog-green-crunch-10kg">
1742                      <div class="relative group overflow-hidden rounded-tnone">
1743                        <img loading="lazy" 
1744                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
1745                          ix-src="https://cdn.quickbutik.com/images/2666L/products/17809207614899f69a69e9cb262c830.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Skadad förpackning! VEGDOG Green Crunch 10kg">
1746                        
1747                        <img loading="lazy"
1748                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
1749                          ix-src="https://cdn.quickbutik.com/images/2666L/products/178092076187c645fc69e9cb1794510.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Skadad förpackning! VEGDOG Green Crunch 10kg">
1750                        
1751                      </div>
1752                    </a>
1753                  
1754                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
1755                      <a class="block px-6 mb-4" href="/hundmat/skadad-forpackning-vegdog-green-crunch-10kg">
1756                        
1757                        <!-- Title -->
1758                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
1759                          Skadad förpackning! VEGDOG Green Crunch 10kg
1760                        </h3>
1761                  
1762                        <!-- Reviews container -->
1763                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
1764                  
1765                      </a>
1766                  
1767                      <!-- Description -->
1768                      <template x-if="layoutSingle">
1769                        
1770                      </template>
1771                  
1772                      <div>
1773                  
1774                        <!-- Price -->
1775                        <div class="px-6">
1776                  
1777                          <!-- Current price -->
1778                          <span>
1779                            <span class="text-xl font-bold text-accent">599 SEK</span>
1780                          </span>
1781                          
1782                          <!-- Original price -->
1783                  
1784                        </div>
1785                  
1786                        <!-- Add to basket -->
1787                        <div class="flex justify-between gap-2 px-6 mt-2">
1788                  
1789                            <!-- Buy button -->
1790                              
1791                              <!-- No options -->
1792                  
1793                              <!-- Has options -->
1794                              <a href="/hundmat/skadad-forpackning-vegdog-green-crunch-10kg" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
1795                  
1796                                <!-- Icon (arrow) -->
1797                  
1798                                <!-- Text -->
1799                                Läs mer
1800                  
1801                              </a>
1802                  
1803                              <!-- Sold out -->
1804                            
1805                  
1806                  
1807                            
1808                            <!-- Link to product when no buy button -->
1809                  
1810                            <!-- Wishlist -->
1811                            <div data-qb-selector="product-item-wishlist" data-product-id="979" class="flex items-center"></div>
1812                  
1813                        </div>
1814                        
1815                        <!-- Stock status -->
1816                        <div class="text-sm px-6 pt-4">
1817                  
1818                          <!-- Sold out -->
1819                  
1820                          <!-- Not sold out -->
1821                          <span data-in-stock-text="I lager"
1822                            data-out-of-stock-text=""
1823                            data-combination-not-exist-text="Variant finns ej">
1824                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
1825                          </span>
1826                  
1827                        </div>
1828                      
1829                      </div>
1830                    </div>
1831                  </div>
1832      
1833      
1834
1835
1836
1837                  
1838
1839                  
1840
1841
1842
1843                  
1844
1845                  
1846                  <div x-data="{ layoutSingle: '4' == '1' }"
1847                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
1848                  
1849                    data-qb-selector="product-item"
1850                    data-product-id="935"
1851                  
1852                    data-pid="935"
1853                    data-s-title="Omni Revitalising Roast 395g"
1854                    data-s-price="69"
1855                    data-price="69"
1856                  >
1857                  
1858                    <!-- Image -->
1859                    <a class="block relative w-full " href="/hundmat/revitalising-roast-395g">
1860                      <div class="relative group overflow-hidden rounded-tnone">
1861                        <img loading="lazy" 
1862                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
1863                          ix-src="https://cdn.quickbutik.com/images/2666L/products/69526d67bde09.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Revitalising Roast 395g">
1864                        
1865                        <img loading="lazy"
1866                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
1867                          ix-src="https://cdn.quickbutik.com/images/2666L/products/69526d6c4dd4c.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Revitalising Roast 395g">
1868                        
1869                      </div>
1870                    </a>
1871                  
1872                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
1873                      <a class="block px-6 mb-4" href="/hundmat/revitalising-roast-395g">
1874                        
1875                        <!-- Title -->
1876                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
1877                          Omni Revitalising Roast 395g
1878                        </h3>
1879                  
1880                        <!-- Reviews container -->
1881                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
1882                  
1883                      </a>
1884                  
1885                      <!-- Description -->
1886                      <template x-if="layoutSingle">
1887                        
1888                      </template>
1889                  
1890                      <div>
1891                  
1892                        <!-- Price -->
1893                        <div class="px-6">
1894                  
1895                          <!-- Current price -->
1896                          <span>
1897                            <span class="text-xl font-bold text-accent">69 SEK</span>
1898                          </span>
1899                          
1900                          <!-- Original price -->
1901                  
1902                        </div>
1903                  
1904                        <!-- Add to basket -->
1905                        <div class="flex justify-between gap-2 px-6 mt-2">
1906                  
1907                            <!-- Buy button -->
1908                              
1909                              <!-- No options -->
1910                              <button @click="$store.cart.add({
1911                                          productID: 935,
1912                                          quantity: 1
1913                                        })"
1914                                aria-label="Lägg i korgen"
1915                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
1916                                
1917                                <!-- Icon (cart) -->
1918                                
1919                                <!-- Text -->
1920                                Lägg i korgen
1921                  
1922                              </button>
1923                  
1924                              <!-- Has options -->
1925                  
1926                              <!-- Sold out -->
1927                            
1928                  
1929                  
1930                            
1931                            <!-- Link to product when no buy button -->
1932                  
1933                            <!-- Wishlist -->
1934                            <div data-qb-selector="product-item-wishlist" data-product-id="935" class="flex items-center"></div>
1935                  
1936                        </div>
1937                        
1938                        <!-- Stock status -->
1939                        <div class="text-sm px-6 pt-4">
1940                  
1941                          <!-- Sold out -->
1942                  
1943                          <!-- Not sold out -->
1944                          <span data-in-stock-text="I lager"
1945                            data-out-of-stock-text=""
1946                            data-combination-not-exist-text="Variant finns ej">
1947                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
1948                          </span>
1949                  
1950                        </div>
1951                      
1952                      </div>
1953                    </div>
1954                  </div>
1955      
1956      
1957
1958
1959
1960                  
1961
1962                  
1963
1964
1965
1966                  
1967
1968                  
1969                  <div x-data="{ layoutSingle: '4' == '1' }"
1970                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
1971                  
1972                    data-qb-selector="product-item"
1973                    data-product-id="934"
1974                  
1975                    data-pid="934"
1976                    data-s-title="Omni Chickpea &amp; Potato Paté 395g"
1977                    data-s-price="69"
1978                    data-price="69"
1979                  >
1980                  
1981                    <!-- Image -->
1982                    <a class="block relative w-full " href="/hundmat/omni-comforting-casserole">
1983                      <div class="relative group overflow-hidden rounded-tnone">
1984                        <img loading="lazy" 
1985                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
1986                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6a4615b07d4bc.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Chickpea &amp; Potato Paté 395g">
1987                        
1988                        <img loading="lazy"
1989                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
1990                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6a4615b674ccb.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Chickpea &amp; Potato Paté 395g">
1991                        
1992                      </div>
1993                    </a>
1994                  
1995                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
1996                      <a class="block px-6 mb-4" href="/hundmat/omni-comforting-casserole">
1997                        
1998                        <!-- Title -->
1999                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
2000                          Omni Chickpea &amp; Potato Paté 395g
2001                        </h3>
2002                  
2003                        <!-- Reviews container -->
2004                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
2005                  
2006                      </a>
2007                  
2008                      <!-- Description -->
2009                      <template x-if="layoutSingle">
2010                        
2011                      </template>
2012                  
2013                      <div>
2014                  
2015                        <!-- Price -->
2016                        <div class="px-6">
2017                  
2018                          <!-- Current price -->
2019                          <span>
2020                            <span class="text-xl font-bold text-accent">69 SEK</span>
2021                          </span>
2022                          
2023                          <!-- Original price -->
2024                  
2025                        </div>
2026                  
2027                        <!-- Add to basket -->
2028                        <div class="flex justify-between gap-2 px-6 mt-2">
2029                  
2030                            <!-- Buy button -->
2031                              
2032                              <!-- No options -->
2033                  
2034                              <!-- Has options -->
2035                  
2036                              <!-- Sold out -->
2037                              <a href="/hundmat/omni-comforting-casserole" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
2038                  
2039                                <!-- Icon -->
2040                  
2041                                <!-- Text -->
2042                                Läs mer
2043                  
2044                              </a>
2045                            
2046                  
2047                  
2048                            
2049                            <!-- Link to product when no buy button -->
2050                  
2051                            <!-- Wishlist -->
2052                            <div data-qb-selector="product-item-wishlist" data-product-id="934" class="flex items-center"></div>
2053                  
2054                        </div>
2055                        
2056                        <!-- Stock status -->
2057                        <div class="text-sm px-6 pt-4">
2058                  
2059                          <!-- Sold out -->
2060                          <span>
2061                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
2062                          </span>
2063                  
2064                          <!-- Not sold out -->
2065                  
2066                        </div>
2067                      
2068                      </div>
2069                    </div>
2070                  </div>
2071      
2072      
2073
2074
2075
2076                  
2077
2078                  
2079
2080
2081
2082                  
2083
2084                  
2085                  <div x-data="{ layoutSingle: '4' == '1' }"
2086                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
2087                  
2088                    data-qb-selector="product-item"
2089                    data-product-id="896"
2090                  
2091                    data-pid="896"
2092                    data-s-title="Presentkort"
2093                    data-s-price="200.00"
2094                    data-price="200.00"
2095                  >
2096                  
2097                    <!-- Image -->
2098                    <a class="block relative w-full " href="/presentkort/presentkort">
2099                      <div class="relative group overflow-hidden rounded-tnone">
2100                        <img loading="lazy" 
2101                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
2102                          ix-src="https://cdn.quickbutik.com/images/2666L/products/68bb00eae2e5f.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Presentkort">
2103                        
2104                        <img loading="lazy"
2105                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
2106                          ix-src="https://cdn.quickbutik.com/images/2666L/products/68bb066cd4c61.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Presentkort">
2107                        
2108                      </div>
2109                    </a>
2110                  
2111                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
2112                      <a class="block px-6 mb-4" href="/presentkort/presentkort">
2113                        
2114                        <!-- Title -->
2115                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
2116                          Presentkort
2117                        </h3>
2118                  
2119                        <!-- Reviews container -->
2120                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
2121                  
2122                      </a>
2123                  
2124                      <!-- Description -->
2125                      <template x-if="layoutSingle">
2126                        
2127                      </template>
2128                  
2129                      <div>
2130                  
2131                        <!-- Price -->
2132                        <div class="px-6">
2133                  
2134                          <!-- Current price -->
2135                          <span>
2136                            <span class="text-xl font-bold text-accent">200 SEK</span>
2137                          </span>
2138                          
2139                          <!-- Original price -->
2140                  
2141                        </div>
2142                  
2143                        <!-- Add to basket -->
2144                        <div class="flex justify-between gap-2 px-6 mt-2">
2145                  
2146                            <!-- Buy button -->
2147                              
2148                              <!-- No options -->
2149                  
2150                              <!-- Has options -->
2151                              <a href="/presentkort/presentkort" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
2152                  
2153                                <!-- Icon (arrow) -->
2154                  
2155                                <!-- Text -->
2156                                Läs mer
2157                  
2158                              </a>
2159                  
2160                              <!-- Sold out -->
2161                            
2162                  
2163                  
2164                            
2165                            <!-- Link to product when no buy button -->
2166                  
2167                            <!-- Wishlist -->
2168                            <div data-qb-selector="product-item-wishlist" data-product-id="896" class="flex items-center"></div>
2169                  
2170                        </div>
2171                        
2172                        <!-- Stock status -->
2173                        <div class="text-sm px-6 pt-4">
2174                  
2175                          <!-- Sold out -->
2176                  
2177                          <!-- Not sold out -->
2178                          <span data-in-stock-text="I lager"
2179                            data-out-of-stock-text=""
2180                            data-combination-not-exist-text="Variant finns ej">
2181                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
2182                          </span>
2183                  
2184                        </div>
2185                      
2186                      </div>
2187                    </div>
2188                  </div>
2189      
2190      
2191
2192
2193
2194                  
2195
2196                  
2197
2198
2199
2200                  
2201
2202                  
2203                  <div x-data="{ layoutSingle: '4' == '1' }"
2204                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
2205                  
2206                    data-qb-selector="product-item"
2207                    data-product-id="881"
2208                  
2209                    data-pid="881"
2210                    data-s-title="AMI DOG Wet PURPLE 400g"
2211                    data-s-price="45"
2212                    data-price="45"
2213                  >
2214                  
2215                    <!-- Image -->
2216                    <a class="block relative w-full " href="/hundmat/ami-dog-wet-purple-400g">
2217                      <div class="relative group overflow-hidden rounded-tnone">
2218                        <img loading="lazy" 
2219                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
2220                          ix-src="https://cdn.quickbutik.com/images/2666L/products/688b456783cc1.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet PURPLE 400g">
2221                        
2222                        <img loading="lazy"
2223                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
2224                          ix-src="https://cdn.quickbutik.com/images/2666L/products/688b463fe9614.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet PURPLE 400g">
2225                        
2226                      </div>
2227                    </a>
2228                  
2229                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
2230                      <a class="block px-6 mb-4" href="/hundmat/ami-dog-wet-purple-400g">
2231                        
2232                        <!-- Title -->
2233                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
2234                          AMI DOG Wet PURPLE 400g
2235                        </h3>
2236                  
2237                        <!-- Reviews container -->
2238                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
2239                  
2240                      </a>
2241                  
2242                      <!-- Description -->
2243                      <template x-if="layoutSingle">
2244                        
2245                      </template>
2246                  
2247                      <div>
2248                  
2249                        <!-- Price -->
2250                        <div class="px-6">
2251                  
2252                          <!-- Current price -->
2253                          <span>
2254                            <span class="text-xl font-bold text-accent">45 SEK</span>
2255                          </span>
2256                          
2257                          <!-- Original price -->
2258                  
2259                        </div>
2260                  
2261                        <!-- Add to basket -->
2262                        <div class="flex justify-between gap-2 px-6 mt-2">
2263                  
2264                            <!-- Buy button -->
2265                              
2266                              <!-- No options -->
2267                              <button @click="$store.cart.add({
2268                                          productID: 881,
2269                                          quantity: 1
2270                                        })"
2271                                aria-label="Lägg i korgen"
2272                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
2273                                
2274                                <!-- Icon (cart) -->
2275                                
2276                                <!-- Text -->
2277                                Lägg i korgen
2278                  
2279                              </button>
2280                  
2281                              <!-- Has options -->
2282                  
2283                              <!-- Sold out -->
2284                            
2285                  
2286                  
2287                            
2288                            <!-- Link to product when no buy button -->
2289                  
2290                            <!-- Wishlist -->
2291                            <div data-qb-selector="product-item-wishlist" data-product-id="881" class="flex items-center"></div>
2292                  
2293                        </div>
2294                        
2295                        <!-- Stock status -->
2296                        <div class="text-sm px-6 pt-4">
2297                  
2298                          <!-- Sold out -->
2299                  
2300                          <!-- Not sold out -->
2301                          <span data-in-stock-text="I lager"
2302                            data-out-of-stock-text=""
2303                            data-combination-not-exist-text="Variant finns ej">
2304                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
2305                          </span>
2306                  
2307                        </div>
2308                      
2309                      </div>
2310                    </div>
2311                  </div>
2312      
2313      
2314
2315
2316
2317                  
2318
2319                  
2320
2321
2322
2323                  
2324
2325                  
2326                  <div x-data="{ layoutSingle: '4' == '1' }"
2327                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
2328                  
2329                    data-qb-selector="product-item"
2330                    data-product-id="880"
2331                  
2332                    data-pid="880"
2333                    data-s-title="Benevo Puppy Original 2kg/10kg"
2334                    data-s-price="269.00"
2335                    data-price="269.00"
2336                  >
2337                  
2338                    <!-- Image -->
2339                    <a class="block relative w-full " href="/hundmat/kopia-kort-datum-benevo-puppy-original-10kg">
2340                      <div class="relative group overflow-hidden rounded-tnone">
2341                        <img loading="lazy" 
2342                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
2343                          ix-src="https://cdn.quickbutik.com/images/2666L/products/175378113817506672156601838004cbd.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Benevo Puppy Original 2kg/10kg">
2344                        
2345                        <img loading="lazy"
2346                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
2347                          ix-src="https://cdn.quickbutik.com/images/2666L/products/1753781139175066721666018381d93e2.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Benevo Puppy Original 2kg/10kg">
2348                        
2349                      </div>
2350                    </a>
2351                  
2352                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
2353                      <a class="block px-6 mb-4" href="/hundmat/kopia-kort-datum-benevo-puppy-original-10kg">
2354                        
2355                        <!-- Title -->
2356                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
2357                          Benevo Puppy Original 2kg/10kg
2358                        </h3>
2359                  
2360                        <!-- Reviews container -->
2361                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
2362                  
2363                      </a>
2364                  
2365                      <!-- Description -->
2366                      <template x-if="layoutSingle">
2367                        
2368                      </template>
2369                  
2370                      <div>
2371                  
2372                        <!-- Price -->
2373                        <div class="px-6">
2374                  
2375                          <!-- Current price -->
2376                          <span>
2377                            <span class="text-xl font-bold text-accent">269 SEK</span>
2378                          </span>
2379                          
2380                          <!-- Original price -->
2381                  
2382                        </div>
2383                  
2384                        <!-- Add to basket -->
2385                        <div class="flex justify-between gap-2 px-6 mt-2">
2386                  
2387                            <!-- Buy button -->
2388                              
2389                              <!-- No options -->
2390                  
2391                              <!-- Has options -->
2392                              <a href="/hundmat/kopia-kort-datum-benevo-puppy-original-10kg" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
2393                  
2394                                <!-- Icon (arrow) -->
2395                  
2396                                <!-- Text -->
2397                                Läs mer
2398                  
2399                              </a>
2400                  
2401                              <!-- Sold out -->
2402                            
2403                  
2404                  
2405                            
2406                            <!-- Link to product when no buy button -->
2407                  
2408                            <!-- Wishlist -->
2409                            <div data-qb-selector="product-item-wishlist" data-product-id="880" class="flex items-center"></div>
2410                  
2411                        </div>
2412                        
2413                        <!-- Stock status -->
2414                        <div class="text-sm px-6 pt-4">
2415                  
2416                          <!-- Sold out -->
2417                  
2418                          <!-- Not sold out -->
2419                          <span data-in-stock-text="I lager"
2420                            data-out-of-stock-text=""
2421                            data-combination-not-exist-text="Variant finns ej">
2422                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
2423                          </span>
2424                  
2425                        </div>
2426                      
2427                      </div>
2428                    </div>
2429                  </div>
2430      
2431      
2432
2433
2434
2435                  
2436
2437                  
2438
2439
2440
2441                  
2442
2443                  
2444                  <div x-data="{ layoutSingle: '4' == '1' }"
2445                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
2446                  
2447                    data-qb-selector="product-item"
2448                    data-product-id="810"
2449                  
2450                    data-pid="810"
2451                    data-s-title="Omni Soothing Stew 395g"
2452                    data-s-price="69"
2453                    data-price="69"
2454                  >
2455                  
2456                    <!-- Image -->
2457                    <a class="block relative w-full " href="/hundmat/omni-soothing-stew-395g">
2458                      <div class="relative group overflow-hidden rounded-tnone">
2459                        <img loading="lazy" 
2460                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
2461                          ix-src="https://cdn.quickbutik.com/images/2666L/products/67c9726886631.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Soothing Stew 395g">
2462                        
2463                        <img loading="lazy"
2464                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
2465                          ix-src="https://cdn.quickbutik.com/images/2666L/products/67c9726e90d6c.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Soothing Stew 395g">
2466                        
2467                      </div>
2468                    </a>
2469                  
2470                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
2471                      <a class="block px-6 mb-4" href="/hundmat/omni-soothing-stew-395g">
2472                        
2473                        <!-- Title -->
2474                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
2475                          Omni Soothing Stew 395g
2476                        </h3>
2477                  
2478                        <!-- Reviews container -->
2479                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
2480                  
2481                      </a>
2482                  
2483                      <!-- Description -->
2484                      <template x-if="layoutSingle">
2485                        
2486                      </template>
2487                  
2488                      <div>
2489                  
2490                        <!-- Price -->
2491                        <div class="px-6">
2492                  
2493                          <!-- Current price -->
2494                          <span>
2495                            <span class="text-xl font-bold text-accent">69 SEK</span>
2496                          </span>
2497                          
2498                          <!-- Original price -->
2499                  
2500                        </div>
2501                  
2502                        <!-- Add to basket -->
2503                        <div class="flex justify-between gap-2 px-6 mt-2">
2504                  
2505                            <!-- Buy button -->
2506                              
2507                              <!-- No options -->
2508                  
2509                              <!-- Has options -->
2510                  
2511                              <!-- Sold out -->
2512                              <a href="/hundmat/omni-soothing-stew-395g" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
2513                  
2514                                <!-- Icon -->
2515                  
2516                                <!-- Text -->
2517                                Läs mer
2518                  
2519                              </a>
2520                            
2521                  
2522                  
2523                            
2524                            <!-- Link to product when no buy button -->
2525                  
2526                            <!-- Wishlist -->
2527                            <div data-qb-selector="product-item-wishlist" data-product-id="810" class="flex items-center"></div>
2528                  
2529                        </div>
2530                        
2531                        <!-- Stock status -->
2532                        <div class="text-sm px-6 pt-4">
2533                  
2534                          <!-- Sold out -->
2535                          <span>
2536                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
2537                          </span>
2538                  
2539                          <!-- Not sold out -->
2540                  
2541                        </div>
2542                      
2543                      </div>
2544                    </div>
2545                  </div>
2546      
2547      
2548
2549
2550
2551                  
2552
2553                  
2554
2555
2556
2557                  
2558
2559                  
2560                  <div x-data="{ layoutSingle: '4' == '1' }"
2561                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
2562                  
2563                    data-qb-selector="product-item"
2564                    data-product-id="809"
2565                  
2566                    data-pid="809"
2567                    data-s-title="Omni PUPPY"
2568                    data-s-price="249.00"
2569                    data-price="249.00"
2570                  >
2571                  
2572                    <!-- Image -->
2573                    <a class="block relative w-full " href="/hundmat/omni-puppy-2kg6kg10kg">
2574                      <div class="relative group overflow-hidden rounded-tnone">
2575                        <img loading="lazy" 
2576                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
2577                          ix-src="https://cdn.quickbutik.com/images/2666L/products/67b9d36f231bf.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni PUPPY">
2578                        
2579                        <img loading="lazy"
2580                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
2581                          ix-src="https://cdn.quickbutik.com/images/2666L/products/67b9d35e40b3a.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni PUPPY">
2582                        
2583                      </div>
2584                    </a>
2585                  
2586                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
2587                      <a class="block px-6 mb-4" href="/hundmat/omni-puppy-2kg6kg10kg">
2588                        
2589                        <!-- Title -->
2590                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
2591                          Omni PUPPY
2592                        </h3>
2593                  
2594                        <!-- Reviews container -->
2595                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
2596                  
2597                      </a>
2598                  
2599                      <!-- Description -->
2600                      <template x-if="layoutSingle">
2601                        
2602                      </template>
2603                  
2604                      <div>
2605                  
2606                        <!-- Price -->
2607                        <div class="px-6">
2608                  
2609                          <!-- Current price -->
2610                          <span>
2611                            <span class="text-xl font-bold text-accent">249 SEK</span>
2612                          </span>
2613                          
2614                          <!-- Original price -->
2615                  
2616                        </div>
2617                  
2618                        <!-- Add to basket -->
2619                        <div class="flex justify-between gap-2 px-6 mt-2">
2620                  
2621                            <!-- Buy button -->
2622                              
2623                              <!-- No options -->
2624                  
2625                              <!-- Has options -->
2626                              <a href="/hundmat/omni-puppy-2kg6kg10kg" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
2627                  
2628                                <!-- Icon (arrow) -->
2629                  
2630                                <!-- Text -->
2631                                Läs mer
2632                  
2633                              </a>
2634                  
2635                              <!-- Sold out -->
2636                            
2637                  
2638                  
2639                            
2640                            <!-- Link to product when no buy button -->
2641                  
2642                            <!-- Wishlist -->
2643                            <div data-qb-selector="product-item-wishlist" data-product-id="809" class="flex items-center"></div>
2644                  
2645                        </div>
2646                        
2647                        <!-- Stock status -->
2648                        <div class="text-sm px-6 pt-4">
2649                  
2650                          <!-- Sold out -->
2651                  
2652                          <!-- Not sold out -->
2653                          <span data-in-stock-text="I lager"
2654                            data-out-of-stock-text=""
2655                            data-combination-not-exist-text="Variant finns ej">
2656                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
2657                          </span>
2658                  
2659                        </div>
2660                      
2661                      </div>
2662                    </div>
2663                  </div>
2664      
2665      
2666
2667
2668
2669                  
2670
2671                  
2672
2673
2674
2675                  
2676
2677                  
2678                  <div x-data="{ layoutSingle: '4' == '1' }"
2679                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
2680                  
2681                    data-qb-selector="product-item"
2682                    data-product-id="805"
2683                  
2684                    data-pid="805"
2685                    data-s-title="Vegan4Dogs Greta 5kg/14kg"
2686                    data-s-price="499.00"
2687                    data-price="499.00"
2688                  >
2689                  
2690                    <!-- Image -->
2691                    <a class="block relative w-full " href="/hundmat/vegan4dogs-greta-5kg14kg">
2692                      <div class="relative group overflow-hidden rounded-tnone">
2693                        <img loading="lazy" 
2694                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
2695                          ix-src="https://cdn.quickbutik.com/images/2666L/products/17393713845ba4dac8b3367.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Vegan4Dogs Greta 5kg/14kg">
2696                        
2697                        <img loading="lazy"
2698                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
2699                          ix-src="https://cdn.quickbutik.com/images/2666L/products/173937138559ee3fc13ecfb.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Vegan4Dogs Greta 5kg/14kg">
2700                        
2701                      </div>
2702                    </a>
2703                  
2704                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
2705                      <a class="block px-6 mb-4" href="/hundmat/vegan4dogs-greta-5kg14kg">
2706                        
2707                        <!-- Title -->
2708                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
2709                          Vegan4Dogs Greta 5kg/14kg
2710                        </h3>
2711                  
2712                        <!-- Reviews container -->
2713                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
2714                  
2715                      </a>
2716                  
2717                      <!-- Description -->
2718                      <template x-if="layoutSingle">
2719                        
2720                      </template>
2721                  
2722                      <div>
2723                  
2724                        <!-- Price -->
2725                        <div class="px-6">
2726                  
2727                          <!-- Current price -->
2728                          <span>
2729                            <span class="text-xl font-bold text-accent">499 SEK</span>
2730                          </span>
2731                          
2732                          <!-- Original price -->
2733                  
2734                        </div>
2735                  
2736                        <!-- Add to basket -->
2737                        <div class="flex justify-between gap-2 px-6 mt-2">
2738                  
2739                            <!-- Buy button -->
2740                              
2741                              <!-- No options -->
2742                  
2743                              <!-- Has options -->
2744                              <a href="/hundmat/vegan4dogs-greta-5kg14kg" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
2745                  
2746                                <!-- Icon (arrow) -->
2747                  
2748                                <!-- Text -->
2749                                Läs mer
2750                  
2751                              </a>
2752                  
2753                              <!-- Sold out -->
2754                            
2755                  
2756                  
2757                            
2758                            <!-- Link to product when no buy button -->
2759                  
2760                            <!-- Wishlist -->
2761                            <div data-qb-selector="product-item-wishlist" data-product-id="805" class="flex items-center"></div>
2762                  
2763                        </div>
2764                        
2765                        <!-- Stock status -->
2766                        <div class="text-sm px-6 pt-4">
2767                  
2768                          <!-- Sold out -->
2769                  
2770                          <!-- Not sold out -->
2771                          <span data-in-stock-text="I lager"
2772                            data-out-of-stock-text=""
2773                            data-combination-not-exist-text="Variant finns ej">
2774                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
2775                          </span>
2776                  
2777                        </div>
2778                      
2779                      </div>
2780                    </div>
2781                  </div>
2782      
2783      
2784
2785
2786
2787                  
2788
2789                  
2790
2791
2792
2793                  
2794
2795                  
2796                  <div x-data="{ layoutSingle: '4' == '1' }"
2797                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
2798                  
2799                    data-qb-selector="product-item"
2800                    data-product-id="793"
2801                  
2802                    data-pid="793"
2803                    data-s-title="Vegdog Simply Crunch 1kg/5kg/10kg"
2804                    data-s-price="129.00"
2805                    data-price="129.00"
2806                  >
2807                  
2808                    <!-- Image -->
2809                    <a class="block relative w-full " href="/hundmat/vegdog-simply-crunch-2kg5kg10kg">
2810                      <div class="relative group overflow-hidden rounded-tnone">
2811                        <img loading="lazy" 
2812                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
2813                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6789195714239.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Vegdog Simply Crunch 1kg/5kg/10kg">
2814                        
2815                        <img loading="lazy"
2816                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
2817                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6789195e5de04.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Vegdog Simply Crunch 1kg/5kg/10kg">
2818                        
2819                      </div>
2820                    </a>
2821                  
2822                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
2823                      <a class="block px-6 mb-4" href="/hundmat/vegdog-simply-crunch-2
2823kg5kg10kg">
2824                        
2825                        <!-- Title -->
2826                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
2827                          Vegdog Simply Crunch 1kg/5kg/10kg
2828                        </h3>
2829                  
2830                        <!-- Reviews container -->
2831                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
2832                  
2833                      </a>
2834                  
2835                      <!-- Description -->
2836                      <template x-if="layoutSingle">
2837                        
2838                      </template>
2839                  
2840                      <div>
2841                  
2842                        <!-- Price -->
2843                        <div class="px-6">
2844                  
2845                          <!-- Current price -->
2846                          <span>
2847                            <span class="text-xl font-bold text-accent">129 SEK</span>
2848                          </span>
2849                          
2850                          <!-- Original price -->
2851                  
2852                        </div>
2853                  
2854                        <!-- Add to basket -->
2855                        <div class="flex justify-between gap-2 px-6 mt-2">
2856                  
2857                            <!-- Buy button -->
2858                              
2859                              <!-- No options -->
2860                  
2861                              <!-- Has options -->
2862                              <a href="/hundmat/vegdog-simply-crunch-2kg5kg10kg" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
2863                  
2864                                <!-- Icon (arrow) -->
2865                  
2866                                <!-- Text -->
2867                                Läs mer
2868                  
2869                              </a>
2870                  
2871                              <!-- Sold out -->
2872                            
2873                  
2874                  
2875                            
2876                            <!-- Link to product when no buy button -->
2877                  
2878                            <!-- Wishlist -->
2879                            <div data-qb-selector="product-item-wishlist" data-product-id="793" class="flex items-center"></div>
2880                  
2881                        </div>
2882                        
2883                        <!-- Stock status -->
2884                        <div class="text-sm px-6 pt-4">
2885                  
2886                          <!-- Sold out -->
2887                  
2888                          <!-- Not sold out -->
2889                          <span data-in-stock-text="I lager"
2890                            data-out-of-stock-text=""
2891                            data-combination-not-exist-text="Variant finns ej">
2892                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
2893                          </span>
2894                  
2895                        </div>
2896                      
2897                      </div>
2898                    </div>
2899                  </div>
2900      
2901      
2902
2903
2904
2905                  
2906
2907                  
2908
2909
2910
2911                  
2912
2913                  
2914                  <div x-data="{ layoutSingle: '4' == '1' }"
2915                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
2916                  
2917                    data-qb-selector="product-item"
2918                    data-product-id="788"
2919                  
2920                    data-pid="788"
2921                    data-s-title="Omni No-Chicken Pot Pie 400g"
2922                    data-s-price="69"
2923                    data-price="69"
2924                  >
2925                  
2926                    <!-- Image -->
2927                    <a class="block relative w-full " href="/hundmat/omni-no-chicken-pot-pie-400g">
2928                      <div class="relative group overflow-hidden rounded-tnone">
2929                        <img loading="lazy" 
2930                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
2931                          ix-src="https://cdn.quickbutik.com/images/2666L/products/671a5ed542493.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni No-Chicken Pot Pie 400g">
2932                        
2933                        <img loading="lazy"
2934                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
2935                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6960c1517f323.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni No-Chicken Pot Pie 400g">
2936                        
2937                      </div>
2938                    </a>
2939                  
2940                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
2941                      <a class="block px-6 mb-4" href="/hundmat/omni-no-chicken-pot-pie-400g">
2942                        
2943                        <!-- Title -->
2944                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
2945                          Omni No-Chicken Pot Pie 400g
2946                        </h3>
2947                  
2948                        <!-- Reviews container -->
2949                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
2950                  
2951                      </a>
2952                  
2953                      <!-- Description -->
2954                      <template x-if="layoutSingle">
2955                        
2956                      </template>
2957                  
2958                      <div>
2959                  
2960                        <!-- Price -->
2961                        <div class="px-6">
2962                  
2963                          <!-- Current price -->
2964                          <span>
2965                            <span class="text-xl font-bold text-accent">69 SEK</span>
2966                          </span>
2967                          
2968                          <!-- Original price -->
2969                  
2970                        </div>
2971                  
2972                        <!-- Add to basket -->
2973                        <div class="flex justify-between gap-2 px-6 mt-2">
2974                  
2975                            <!-- Buy button -->
2976                              
2977                              <!-- No options -->
2978                  
2979                              <!-- Has options -->
2980                  
2981                              <!-- Sold out -->
2982                              <a href="/hundmat/omni-no-chicken-pot-pie-400g" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
2983                  
2984                                <!-- Icon -->
2985                  
2986                                <!-- Text -->
2987                                Läs mer
2988                  
2989                              </a>
2990                            
2991                  
2992                  
2993                            
2994                            <!-- Link to product when no buy button -->
2995                  
2996                            <!-- Wishlist -->
2997                            <div data-qb-selector="product-item-wishlist" data-product-id="788" class="flex items-center"></div>
2998                  
2999                        </div>
3000                        
3001                        <!-- Stock status -->
3002                        <div class="text-sm px-6 pt-4">
3003                  
3004                          <!-- Sold out -->
3005                          <span>
3006                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
3007                          </span>
3008                  
3009                          <!-- Not sold out -->
3010                  
3011                        </div>
3012                      
3013                      </div>
3014                    </div>
3015                  </div>
3016      
3017      
3018
3019
3020
3021                  
3022
3023                  
3024
3025
3026
3027                  
3028
3029                  
3030                  <div x-data="{ layoutSingle: '4' == '1' }"
3031                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
3032                  
3033                    data-qb-selector="product-item"
3034                    data-product-id="787"
3035                  
3036                    data-pid="787"
3037                    data-s-title="Omni No-Beef Casserole 400g"
3038                    data-s-price="69"
3039                    data-price="69"
3040                  >
3041                  
3042                    <!-- Image -->
3043                    <a class="block relative w-full " href="/hundmat/omni-no-beef-casserole-400g">
3044                      <div class="relative group overflow-hidden rounded-tnone">
3045                        <img loading="lazy" 
3046                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
3047                          ix-src="https://cdn.quickbutik.com/images/2666L/products/671a5ca798cc9.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni No-Beef Casserole 400g">
3048                        
3049                        <img loading="lazy"
3050                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
3051                          ix-src="https://cdn.quickbutik.com/images/2666L/products/671b4541d1a10.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni No-Beef Casserole 400g">
3052                        
3053                      </div>
3054                    </a>
3055                  
3056                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
3057                      <a class="block px-6 mb-4" href="/hundmat/omni-no-beef-casserole-400g">
3058                        
3059                        <!-- Title -->
3060                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
3061                          Omni No-Beef Casserole 400g
3062                        </h3>
3063                  
3064                        <!-- Reviews container -->
3065                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
3066                  
3067                      </a>
3068                  
3069                      <!-- Description -->
3070                      <template x-if="layoutSingle">
3071                        
3072                      </template>
3073                  
3074                      <div>
3075                  
3076                        <!-- Price -->
3077                        <div class="px-6">
3078                  
3079                          <!-- Current price -->
3080                          <span>
3081                            <span class="text-xl font-bold text-accent">69 SEK</span>
3082                          </span>
3083                          
3084                          <!-- Original price -->
3085                  
3086                        </div>
3087                  
3088                        <!-- Add to basket -->
3089                        <div class="flex justify-between gap-2 px-6 mt-2">
3090                  
3091                            <!-- Buy button -->
3092                              
3093                              <!-- No options -->
3094                  
3095                              <!-- Has options -->
3096                  
3097                              <!-- Sold out -->
3098                              <a href="/hundmat/omni-no-beef-casserole-400g" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
3099                  
3100                                <!-- Icon -->
3101                  
3102                                <!-- Text -->
3103                                Läs mer
3104                  
3105                              </a>
3106                            
3107                  
3108                  
3109                            
3110                            <!-- Link to product when no buy button -->
3111                  
3112                            <!-- Wishlist -->
3113                            <div data-qb-selector="product-item-wishlist" data-product-id="787" class="flex items-center"></div>
3114                  
3115                        </div>
3116                        
3117                        <!-- Stock status -->
3118                        <div class="text-sm px-6 pt-4">
3119                  
3120                          <!-- Sold out -->
3121                          <span>
3122                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
3123                          </span>
3124                  
3125                          <!-- Not sold out -->
3126                  
3127                        </div>
3128                      
3129                      </div>
3130                    </div>
3131                  </div>
3132      
3133      
3134
3135
3136
3137                  
3138
3139                  
3140
3141
3142
3143                  
3144
3145                  
3146                  <div x-data="{ layoutSingle: '4' == '1' }"
3147                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
3148                  
3149                    data-qb-selector="product-item"
3150                    data-product-id="772"
3151                  
3152                    data-pid="772"
3153                    data-s-title="Pawr Healthy Habits 2,5kg/8kg"
3154                    data-s-price="349.00"
3155                    data-price="349.00"
3156                  >
3157                  
3158                    <!-- Image -->
3159                    <a class="block relative w-full " href="/hundmat/pawr-healthy-habits-25kg8kg">
3160                      <div class="relative group overflow-hidden rounded-tnone">
3161                        <img loading="lazy" 
3162                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
3163                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66f697264d9a5.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Pawr Healthy Habits 2,5kg/8kg">
3164                        
3165                        <img loading="lazy"
3166                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
3167                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66f6972f09fd7.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Pawr Healthy Habits 2,5kg/8kg">
3168                        
3169                      </div>
3170                    </a>
3171                  
3172                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
3173                      <a class="block px-6 mb-4" href="/hundmat/pawr-healthy-habits-25kg8kg">
3174                        
3175                        <!-- Title -->
3176                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
3177                          Pawr Healthy Habits 2,5kg/8kg
3178                        </h3>
3179                  
3180                        <!-- Reviews container -->
3181                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
3182                  
3183                      </a>
3184                  
3185                      <!-- Description -->
3186                      <template x-if="layoutSingle">
3187                        
3188                      </template>
3189                  
3190                      <div>
3191                  
3192                        <!-- Price -->
3193                        <div class="px-6">
3194                  
3195                          <!-- Current price -->
3196                          <span>
3197                            <span class="text-xl font-bold text-accent">349 SEK</span>
3198                          </span>
3199                          
3200                          <!-- Original price -->
3201                  
3202                        </div>
3203                  
3204                        <!-- Add to basket -->
3205                        <div class="flex justify-between gap-2 px-6 mt-2">
3206                  
3207                            <!-- Buy button -->
3208                              
3209                              <!-- No options -->
3210                  
3211                              <!-- Has options -->
3212                  
3213                              <!-- Sold out -->
3214                              <a href="/hundmat/pawr-healthy-habits-25kg8kg" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
3215                  
3216                                <!-- Icon -->
3217                  
3218                                <!-- Text -->
3219                                Läs mer
3220                  
3221                              </a>
3222                            
3223                  
3224                  
3225                            
3226                            <!-- Link to product when no buy button -->
3227                  
3228                            <!-- Wishlist -->
3229                            <div data-qb-selector="product-item-wishlist" data-product-id="772" class="flex items-center"></div>
3230                  
3231                        </div>
3232                        
3233                        <!-- Stock status -->
3234                        <div class="text-sm px-6 pt-4">
3235                  
3236                          <!-- Sold out -->
3237                          <span>
3238                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
3239                          </span>
3240                  
3241                          <!-- Not sold out -->
3242                  
3243                        </div>
3244                      
3245                      </div>
3246                    </div>
3247                  </div>
3248      
3249      
3250
3251
3252
3253                  
3254
3255                  
3256
3257
3258
3259                  
3260
3261                  
3262                  <div x-data="{ layoutSingle: '4' == '1' }"
3263                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
3264                  
3265                    data-qb-selector="product-item"
3266                    data-product-id="771"
3267                  
3268                    data-pid="771"
3269                    data-s-title="Pawr Green Glory 750g"
3270                    data-s-price="129"
3271                    data-price="129"
3272                  >
3273                  
3274                    <!-- Image -->
3275                    <a class="block relative w-full " href="/hundmat/pawr-green-glory-750g">
3276                      <div class="relative group overflow-hidden rounded-tnone">
3277                        <img loading="lazy" 
3278                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
3279                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66f694662ba95.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Pawr Green Glory 750g">
3280                        
3281                        <img loading="lazy"
3282                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
3283                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66f694721865f.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Pawr Green Glory 750g">
3284                        
3285                      </div>
3286                    </a>
3287                  
3288                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
3289                      <a class="block px-6 mb-4" href="/hundmat/pawr-green-glory-750g">
3290                        
3291                        <!-- Title -->
3292                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
3293                          Pawr Green Glory 750g
3294                        </h3>
3295                  
3296                        <!-- Reviews container -->
3297                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
3298                  
3299                      </a>
3300                  
3301                      <!-- Description -->
3302                      <template x-if="layoutSingle">
3303                        
3304                      </template>
3305                  
3306                      <div>
3307                  
3308                        <!-- Price -->
3309                        <div class="px-6">
3310                  
3311                          <!-- Current price -->
3312                          <span>
3313                            <span class="text-xl font-bold text-accent">129 SEK</span>
3314                          </span>
3315                          
3316                          <!-- Original price -->
3317                  
3318                        </div>
3319                  
3320                        <!-- Add to basket -->
3321                        <div class="flex justify-between gap-2 px-6 mt-2">
3322                  
3323                            <!-- Buy button -->
3324                              
3325                              <!-- No options -->
3326                              <button @click="$store.cart.add({
3327                                          productID: 771,
3328                                          quantity: 1
3329                                        })"
3330                                aria-label="Lägg i korgen"
3331                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
3332                                
3333                                <!-- Icon (cart) -->
3334                                
3335                                <!-- Text -->
3336                                Lägg i korgen
3337                  
3338                              </button>
3339                  
3340                              <!-- Has options -->
3341                  
3342                              <!-- Sold out -->
3343                            
3344                  
3345                  
3346                            
3347                            <!-- Link to product when no buy button -->
3348                  
3349                            <!-- Wishlist -->
3350                            <div data-qb-selector="product-item-wishlist" data-product-id="771" class="flex items-center"></div>
3351                  
3352                        </div>
3353                        
3354                        <!-- Stock status -->
3355                        <div class="text-sm px-6 pt-4">
3356                  
3357                          <!-- Sold out -->
3358                  
3359                          <!-- Not sold out -->
3360                          <span data-in-stock-text="I lager"
3361                            data-out-of-stock-text=""
3362                            data-combination-not-exist-text="Variant finns ej">
3363                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
3364                          </span>
3365                  
3366                        </div>
3367                      
3368                      </div>
3369                    </div>
3370                  </div>
3371      
3372      
3373
3374
3375
3376                  
3377
3378                  
3379
3380
3381
3382                  
3383
3384                  
3385                  <div x-data="{ layoutSingle: '4' == '1' }"
3386                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
3387                  
3388                    data-qb-selector="product-item"
3389                    data-product-id="770"
3390                  
3391                    data-pid="770"
3392                    data-s-title="Pawr Beet Booster 750g"
3393                    data-s-price="129"
3394                    data-price="129"
3395                  >
3396                  
3397                    <!-- Image -->
3398                    <a class="block relative w-full " href="/hundmat/pawr-beet-booster-750g">
3399                      <div class="relative group overflow-hidden rounded-tnone">
3400                        <img loading="lazy" 
3401                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
3402                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66f5a6eee6c4c.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Pawr Beet Booster 750g">
3403                        
3404                        <img loading="lazy"
3405                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
3406                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66f5a6fd1cb14.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Pawr Beet Booster 750g">
3407                        
3408                      </div>
3409                    </a>
3410                  
3411                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
3412                      <a class="block px-6 mb-4" href="/hundmat/pawr-beet-booster-750g">
3413                        
3414                        <!-- Title -->
3415                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
3416                          Pawr Beet Booster 750g
3417                        </h3>
3418                  
3419                        <!-- Reviews container -->
3420                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
3421                  
3422                      </a>
3423                  
3424                      <!-- Description -->
3425                      <template x-if="layoutSingle">
3426                        
3427                      </template>
3428                  
3429                      <div>
3430                  
3431                        <!-- Price -->
3432                        <div class="px-6">
3433                  
3434                          <!-- Current price -->
3435                          <span>
3436                            <span class="text-xl font-bold text-accent">129 SEK</span>
3437                          </span>
3438                          
3439                          <!-- Original price -->
3440                  
3441                        </div>
3442                  
3443                        <!-- Add to basket -->
3444                        <div class="flex justify-between gap-2 px-6 mt-2">
3445                  
3446                            <!-- Buy button -->
3447                              
3448                              <!-- No options -->
3449                              <button @click="$store.cart.add({
3450                                          productID: 770,
3451                                          quantity: 1
3452                                        })"
3453                                aria-label="Lägg i korgen"
3454                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
3455                                
3456                                <!-- Icon (cart) -->
3457                                
3458                                <!-- Text -->
3459                                Lägg i korgen
3460                  
3461                              </button>
3462                  
3463                              <!-- Has options -->
3464                  
3465                              <!-- Sold out -->
3466                            
3467                  
3468                  
3469                            
3470                            <!-- Link to product when no buy button -->
3471                  
3472                            <!-- Wishlist -->
3473                            <div data-qb-selector="product-item-wishlist" data-product-id="770" class="flex items-center"></div>
3474                  
3475                        </div>
3476                        
3477                        <!-- Stock status -->
3478                        <div class="text-sm px-6 pt-4">
3479                  
3480                          <!-- Sold out -->
3481                  
3482                          <!-- Not sold out -->
3483                          <span data-in-stock-text="I lager"
3484                            data-out-of-stock-text=""
3485                            data-combination-not-exist-text="Variant finns ej">
3486                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
3487                          </span>
3488                  
3489                        </div>
3490                      
3491                      </div>
3492                    </div>
3493                  </div>
3494      
3495      
3496
3497
3498
3499                  
3500
3501                  
3502
3503
3504
3505                  
3506
3507                  
3508                  <div x-data="{ layoutSingle: '4' == '1' }"
3509                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
3510                  
3511                    data-qb-selector="product-item"
3512                    data-product-id="769"
3513                  
3514                    data-pid="769"
3515                    data-s-title="Pawr Golden Glow 750g"
3516                    data-s-price="129"
3517                    data-price="129"
3518                  >
3519                  
3520                    <!-- Image -->
3521                    <a class="block relative w-full " href="/hundmat/pawr-golden-glow-750g">
3522                      <div class="relative group overflow-hidden rounded-tnone">
3523                        <img loading="lazy" 
3524                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
3525                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66f570e9949c3.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Pawr Golden Glow 750g">
3526                        
3527                        <img loading="lazy"
3528                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
3529                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66f570f2d49e8.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Pawr Golden Glow 750g">
3530                        
3531                      </div>
3532                    </a>
3533                  
3534                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
3535                      <a class="block px-6 mb-4" href="/hundmat/pawr-golden-glow-750g">
3536                        
3537                        <!-- Title -->
3538                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
3539                          Pawr Golden Glow 750g
3540                        </h3>
3541                  
3542                        <!-- Reviews container -->
3543                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
3544                  
3545                      </a>
3546                  
3547                      <!-- Description -->
3548                      <template x-if="layoutSingle">
3549                        
3550                      </template>
3551                  
3552                      <div>
3553                  
3554                        <!-- Price -->
3555                        <div class="px-6">
3556                  
3557                          <!-- Current price -->
3558                          <span>
3559                            <span class="text-xl font-bold text-accent">129 SEK</span>
3560                          </span>
3561                          
3562                          <!-- Original price -->
3563                  
3564                        </div>
3565                  
3566                        <!-- Add to basket -->
3567                        <div class="flex justify-between gap-2 px-6 mt-2">
3568                  
3569                            <!-- Buy button -->
3570                              
3571                              <!-- No options -->
3572                              <button @click="$store.cart.add({
3573                                          productID: 769,
3574                                          quantity: 1
3575                                        })"
3576                                aria-label="Lägg i korgen"
3577                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
3578                                
3579                                <!-- Icon (cart) -->
3580                                
3581                                <!-- Text -->
3582                                Lägg i korgen
3583                  
3584                              </button>
3585                  
3586                              <!-- Has options -->
3587                  
3588                              <!-- Sold out -->
3589                            
3590                  
3591                  
3592                            
3593                            <!-- Link to product when no buy button -->
3594                  
3595                            <!-- Wishlist -->
3596                            <div data-qb-selector="product-item-wishlist" data-product-id="769" class="flex items-center"></div>
3597                  
3598                        </div>
3599                        
3600                        <!-- Stock status -->
3601                        <div class="text-sm px-6 pt-4">
3602                  
3603                          <!-- Sold out -->
3604                  
3605                          <!-- Not sold out -->
3606                          <span data-in-stock-text="I lager"
3607                            data-out-of-stock-text=""
3608                            data-combination-not-exist-text="Variant finns ej">
3609                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
3610                          </span>
3611                  
3612                        </div>
3613                      
3614                      </div>
3615                    </div>
3616                  </div>
3617      
3618      
3619
3620
3621
3622                  
3623
3624                  
3625
3626
3627
3628                  
3629
3630                  
3631                  <div x-data="{ layoutSingle: '4' == '1' }"
3632                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
3633                  
3634                    data-qb-selector="product-item"
3635                    data-product-id="763"
3636                  
3637                    data-pid="763"
3638                    data-s-title="Omni Toppers 150g"
3639                    data-s-price="45.00"
3640                    data-price="45.00"
3641                  >
3642                  
3643                    <!-- Image -->
3644                    <a class="block relative w-full " href="/hundmat/37-omni-no-beef-bites-toppers">
3645                      <div class="relative group overflow-hidden rounded-tnone">
3646                        <img loading="lazy" 
3647                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
3648                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66e91cfeb969e.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Toppers 150g">
3649                        
3650                        <img loading="lazy"
3651                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
3652                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66e91d1ec37b7.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni Toppers 150g">
3653                        
3654                      </div>
3655                    </a>
3656                  
3657                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
3658                      <a class="block px-6 mb-4" href="/hundmat/37-omni-no-beef-bites-toppers">
3659                        
3660                        <!-- Title -->
3661                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
3662                          Omni Toppers 150g
3663                        </h3>
3664                  
3665                        <!-- Reviews container -->
3666                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
3667                  
3668                      </a>
3669                  
3670                      <!-- Description -->
3671                      <template x-if="layoutSingle">
3672                        
3673                      </template>
3674                  
3675                      <div>
3676                  
3677                        <!-- Price -->
3678                        <div class="px-6">
3679                  
3680                          <!-- Current price -->
3681                          <span>
3682                            <span class="text-xl font-bold text-accent">45 SEK</span>
3683                          </span>
3684                          
3685                          <!-- Original price -->
3686                  
3687                        </div>
3688                  
3689                        <!-- Add to basket -->
3690                        <div class="flex justify-between gap-2 px-6 mt-2">
3691                  
3692                            <!-- Buy button -->
3693                              
3694                              <!-- No options -->
3695                  
3696                              <!-- Has options -->
3697                  
3698                              <!-- Sold out -->
3699                              <a href="/hundmat/37-omni-no-beef-bites-toppers" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
3700                  
3701                                <!-- Icon -->
3702                  
3703                                <!-- Text -->
3704                                Läs mer
3705                  
3706                              </a>
3707                            
3708                  
3709                  
3710                            
3711                            <!-- Link to product when no buy button -->
3712                  
3713                            <!-- Wishlist -->
3714                            <div data-qb-selector="product-item-wishlist" data-product-id="763" class="flex items-center"></div>
3715                  
3716                        </div>
3717                        
3718                        <!-- Stock status -->
3719                        <div class="text-sm px-6 pt-4">
3720                  
3721                          <!-- Sold out -->
3722                          <span>
3723                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
3724                          </span>
3725                  
3726                          <!-- Not sold out -->
3727                  
3728                        </div>
3729                      
3730                      </div>
3731                    </div>
3732                  </div>
3733      
3734      
3735
3736
3737
3738                  
3739
3740                  
3741
3742
3743
3744                  
3745
3746                  
3747                  <div x-data="{ layoutSingle: '4' == '1' }"
3748                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
3749                  
3750                    data-qb-selector="product-item"
3751                    data-product-id="740"
3752                  
3753                    data-pid="740"
3754                    data-s-title="V-dog Traditional Flakes 15kg"
3755                    data-s-price="999"
3756                    data-price="999"
3757                  >
3758                  
3759                    <!-- Image -->
3760                    <a class="block relative w-full " href="/hundmat/v-dog-traditional-flakes">
3761                      <div class="relative group overflow-hidden rounded-tnone">
3762                        <img loading="lazy" 
3763                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
3764                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65d8805c77f98.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="V-dog Traditional Flakes 15kg">
3765                        
3766                        <img loading="lazy"
3767                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
3768                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65d884ec3e884.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="V-dog Traditional Flakes 15kg">
3769                        
3770                      </div>
3771                    </a>
3772                  
3773                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
3774                      <a class="block px-6 mb-4" href="/hundmat/v-dog-traditional-flakes">
3775                        
3776                        <!-- Title -->
3777                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
3778                          V-dog Traditional Flakes 15kg
3779                        </h3>
3780                  
3781                        <!-- Reviews container -->
3782                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
3783                  
3784                      </a>
3785                  
3786                      <!-- Description -->
3787                      <template x-if="layoutSingle">
3788                        
3789                      </template>
3790                  
3791                      <div>
3792                  
3793                        <!-- Price -->
3794                        <div class="px-6">
3795                  
3796                          <!-- Current price -->
3797                          <span>
3798                            <span class="text-xl font-bold text-accent">999 SEK</span>
3799                          </span>
3800                          
3801                          <!-- Original price -->
3802                  
3803                        </div>
3804                  
3805                        <!-- Add to basket -->
3806                        <div class="flex justify-between gap-2 px-6 mt-2">
3807                  
3808                            <!-- Buy button -->
3809                              
3810                              <!-- No options -->
3811                              <button @click="$store.cart.add({
3812                                          productID: 740,
3813                                          quantity: 1
3814                                        })"
3815                                aria-label="Lägg i korgen"
3816                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
3817                                
3818                                <!-- Icon (cart) -->
3819                                
3820                                <!-- Text -->
3821                                Lägg i korgen
3822                  
3823                              </button>
3824                  
3825                              <!-- Has options -->
3826                  
3827                              <!-- Sold out -->
3828                            
3829                  
3830                  
3831                            
3832                            <!-- Link to product when no buy button -->
3833                  
3834                            <!-- Wishlist -->
3835                            <div data-qb-selector="product-item-wishlist" data-product-id="740" class="flex items-center"></div>
3836                  
3837                        </div>
3838                        
3839                        <!-- Stock status -->
3840                        <div class="text-sm px-6 pt-4">
3841                  
3842                          <!-- Sold out -->
3843                  
3844                          <!-- Not sold out -->
3845                          <span data-in-stock-text="I lager"
3846                            data-out-of-stock-text=""
3847                            data-combination-not-exist-text="Variant finns ej">
3848                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
3849                          </span>
3850                  
3851                        </div>
3852                      
3853                      </div>
3854                    </div>
3855                  </div>
3856      
3857      
3858
3859
3860
3861                  
3862
3863                  
3864
3865
3866
3867                  
3868
3869                  
3870                  <div x-data="{ layoutSingle: '4' == '1' }"
3871                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
3872                  
3873                    data-qb-selector="product-item"
3874                    data-product-id="733"
3875                  
3876                    data-pid="733"
3877                    data-s-title="Omni SENIOR 2kg/6kg/10kg"
3878                    data-s-price="249.00"
3879                    data-price="249.00"
3880                  >
3881                  
3882                    <!-- Image -->
3883                    <a class="block relative w-full " href="/hundmat/omni-senior">
3884                      <div class="relative group overflow-hidden rounded-tnone">
3885                        <img loading="lazy" 
3886                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
3887                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65c3705bdb389.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni SENIOR 2kg/6kg/10kg">
3888                        
3889                        <img loading="lazy"
3890                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
3891                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65c3705bc9699.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni SENIOR 2kg/6kg/10kg">
3892                        
3893                      </div>
3894                    </a>
3895                  
3896                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
3897                      <a class="block px-6 mb-4" href="/hundmat/omni-senior">
3898                        
3899                        <!-- Title -->
3900                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
3901                          Omni SENIOR 2kg/6kg/10kg
3902                        </h3>
3903                  
3904                        <!-- Reviews container -->
3905                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
3906                  
3907                      </a>
3908                  
3909                      <!-- Description -->
3910                      <template x-if="layoutSingle">
3911                        
3912                      </template>
3913                  
3914                      <div>
3915                  
3916                        <!-- Price -->
3917                        <div class="px-6">
3918                  
3919                          <!-- Current price -->
3920                          <span>
3921                            <span class="text-xl font-bold text-accent">249 SEK</span>
3922                          </span>
3923                          
3924                          <!-- Original price -->
3925                  
3926                        </div>
3927                  
3928                        <!-- Add to basket -->
3929                        <div class="flex justify-between gap-2 px-6 mt-2">
3930                  
3931                            <!-- Buy button -->
3932                              
3933                              <!-- No options -->
3934                  
3935                              <!-- Has options -->
3936                  
3937                              <!-- Sold out -->
3938                              <a href="/hundmat/omni-senior" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
3939                  
3940                                <!-- Icon -->
3941                  
3942                                <!-- Text -->
3943                                Läs mer
3944                  
3945                              </a>
3946                            
3947                  
3948                  
3949                            
3950                            <!-- Link to product when no buy button -->
3951                  
3952                            <!-- Wishlist -->
3953                            <div data-qb-selector="product-item-wishlist" data-product-id="733" class="flex items-center"></div>
3954                  
3955                        </div>
3956                        
3957                        <!-- Stock status -->
3958                        <div class="text-sm px-6 pt-4">
3959                  
3960                          <!-- Sold out -->
3961                          <span>
3962                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
3963                          </span>
3964                  
3965                          <!-- Not sold out -->
3966                  
3967                        </div>
3968                      
3969                      </div>
3970                    </div>
3971                  </div>
3972      
3973      
3974
3975
3976
3977                  
3978
3979                  
3980
3981
3982
3983                  
3984
3985                  
3986                  <div x-data="{ layoutSingle: '4' == '1' }"
3987                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
3988                  
3989                    data-qb-selector="product-item"
3990                    data-product-id="732"
3991                  
3992                    data-pid="732"
3993                    data-s-title="Omni ADULT 2kg/6kg/10kg"
3994                    data-s-price="249.00"
3995                    data-price="249.00"
3996                  >
3997                  
3998                    <!-- Image -->
3999                    <a class="block relative w-full " href="/hundmat/omni-pet-adult">
4000                      <div class="relative group overflow-hidden rounded-tnone">
4001                        <img loading="lazy" 
4002                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
4003                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65c36d70d5651.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni ADULT 2kg/6kg/10kg">
4004                        
4005                        <img loading="lazy"
4006                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
4007                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65c36d7177f61.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Omni ADULT 2kg/6kg/10kg">
4008                        
4009                      </div>
4010                    </a>
4011                  
4012                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
4013                      <a class="block px-6 mb-4" href="/hundmat/omni-pet-adult">
4014                        
4015                        <!-- Title -->
4016                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
4017                          Omni ADULT 2kg/6kg/10kg
4018                        </h3>
4019                  
4020                        <!-- Reviews container -->
4021                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
4022                  
4023                      </a>
4024                  
4025                      <!-- Description -->
4026                      <template x-if="layoutSingle">
4027                        
4028                      </template>
4029                  
4030                      <div>
4031                  
4032                        <!-- Price -->
4033                        <div class="px-6">
4034                  
4035                          <!-- Current price -->
4036                          <span>
4037                            <span class="text-xl font-bold text-accent">249 SEK</span>
4038                          </span>
4039                          
4040                          <!-- Original price -->
4041                  
4042                        </div>
4043                  
4044                        <!-- Add to basket -->
4045                        <div class="flex justify-between gap-2 px-6 mt-2">
4046                  
4047                            <!-- Buy button -->
4048                              
4049                              <!-- No options -->
4050                  
4051                              <!-- Has options -->
4052                              <a href="/hundmat/omni-pet-adult" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
4053                  
4054                                <!-- Icon (arrow) -->
4055                  
4056                                <!-- Text -->
4057                                Läs mer
4058                  
4059                              </a>
4060                  
4061                              <!-- Sold out -->
4062                            
4063                  
4064                  
4065                            
4066                            <!-- Link to product when no buy button -->
4067                  
4068                            <!-- Wishlist -->
4069                            <div data-qb-selector="product-item-wishlist" data-product-id="732" class="flex items-center"></div>
4070                  
4071                        </div>
4072                        
4073                        <!-- Stock status -->
4074                        <div class="text-sm px-6 pt-4">
4075                  
4076                          <!-- Sold out -->
4077                  
4078                          <!-- Not sold out -->
4079                          <span data-in-stock-text="I lager"
4080                            data-out-of-stock-text=""
4081                            data-combination-not-exist-text="Variant finns ej">
4082                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
4083                          </span>
4084                  
4085                        </div>
4086                      
4087                      </div>
4088                    </div>
4089                  </div>
4090      
4091      
4092
4093
4094
4095                  
4096
4097                  
4098
4099
4100
4101                  
4102
4103                  
4104                  <div x-data="{ layoutSingle: '4' == '1' }"
4105                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
4106                  
4107                    data-qb-selector="product-item"
4108                    data-product-id="694"
4109                  
4110                    data-pid="694"
4111                    data-s-title="VEGDOG Farmer&#039;s Crunch 1kg/4kg/10kg"
4112                    data-s-price="149.00"
4113                    data-price="149.00"
4114                  >
4115                  
4116                    <!-- Image -->
4117                    <a class="block relative w-full " href="/hundmat/vegdog-farmers-crunch">
4118                      <div class="relative group overflow-hidden rounded-tnone">
4119                        <img loading="lazy" 
4120                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
4121                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a117c1bdba.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Farmer&#039;s Crunch 1kg/4kg/10kg">
4122                        
4123                        <img loading="lazy"
4124                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
4125                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a117c07f68.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Farmer&#039;s Crunch 1kg/4kg/10kg">
4126                        
4127                      </div>
4128                    </a>
4129                  
4130                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
4131                      <a class="block px-6 mb-4" href="/hundmat/vegdog-farmers-crunch">
4132                        
4133                        <!-- Title -->
4134                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
4135                          VEGDOG Farmer&#039;s Crunch 1kg/4kg/10kg
4136                        </h3>
4137                  
4138                        <!-- Reviews container -->
4139                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
4140                  
4141                      </a>
4142                  
4143                      <!-- Description -->
4144                      <template x-if="layoutSingle">
4145                        
4146                      </template>
4147                  
4148                      <div>
4149                  
4150                        <!-- Price -->
4151                        <div class="px-6">
4152                  
4153                          <!-- Current price -->
4154                          <span>
4155                            <span class="text-xl font-bold text-accent">149 SEK</span>
4156                          </span>
4157                          
4158                          <!-- Original price -->
4159                  
4160                        </div>
4161                  
4162                        <!-- Add to basket -->
4163                        <div class="flex justify-between gap-2 px-6 mt-2">
4164                  
4165                            <!-- Buy button -->
4166                              
4167                              <!-- No options -->
4168                  
4169                              <!-- Has options -->
4170                              <a href="/hundmat/vegdog-farmers-crunch" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
4171                  
4172                                <!-- Icon (arrow) -->
4173                  
4174                                <!-- Text -->
4175                                Läs mer
4176                  
4177                              </a>
4178                  
4179                              <!-- Sold out -->
4180                            
4181                  
4182                  
4183                            
4184                            <!-- Link to product when no buy button -->
4185                  
4186                            <!-- Wishlist -->
4187                            <div data-qb-selector="product-item-wishlist" data-product-id="694" class="flex items-center"></div>
4188                  
4189                        </div>
4190                        
4191                        <!-- Stock status -->
4192                        <div class="text-sm px-6 pt-4">
4193                  
4194                          <!-- Sold out -->
4195                  
4196                          <!-- Not sold out -->
4197                          <span data-in-stock-text="I lager"
4198                            data-out-of-stock-text=""
4199                            data-combination-not-exist-text="Variant finns ej">
4200                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
4201                          </span>
4202                  
4203                        </div>
4204                      
4205                      </div>
4206                    </div>
4207                  </div>
4208      
4209      
4210
4211
4212
4213                  
4214
4215                  
4216
4217
4218
4219                  
4220
4221                  
4222                  <div x-data="{ layoutSingle: '4' == '1' }"
4223                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
4224                  
4225                    data-qb-selector="product-item"
4226                    data-product-id="673"
4227                  
4228                    data-pid="673"
4229                    data-s-title="HOWND / Blueberry &amp; Coconut Porridge with Chia &amp; Oats 400g"
4230                    data-s-price="55"
4231                    data-price="55"
4232                  >
4233                  
4234                    <!-- Image -->
4235                    <a class="block relative w-full " href="/hundmat/hownd-blueberry-coconut-porridge-400g">
4236                      <div class="relative group overflow-hidden rounded-tnone">
4237                        <img loading="lazy" 
4238                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
4239                          ix-src="https://cdn.quickbutik.com/images/2666L/products/697a5f76913be.webp" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="HOWND / Blueberry &amp; Coconut Porridge with Chia &amp; Oats 400g">
4240                        
4241                        <img loading="lazy"
4242                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
4243                          ix-src="https://cdn.quickbutik.com/images/2666L/products/697a5f7be924c.webp" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="HOWND / Blueberry &amp; Coconut Porridge with Chia &amp; Oats 400g">
4244                        
4245                      </div>
4246                    </a>
4247                  
4248                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
4249                      <a class="block px-6 mb-4" href="/hundmat/hownd-blueberry-coconut-porridge-400g">
4250                        
4251                        <!-- Title -->
4252                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
4253                          HOWND / Blueberry &amp; Coconut Porridge with Chia &amp; Oats 400g
4254                        </h3>
4255                  
4256                        <!-- Reviews container -->
4257                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
4258                  
4259                      </a>
4260                  
4261                      <!-- Description -->
4262                      <template x-if="layoutSingle">
4263                        
4264                      </template>
4265                  
4266                      <div>
4267                  
4268                        <!-- Price -->
4269                        <div class="px-6">
4270                  
4271                          <!-- Current price -->
4272                          <span>
4273                            <span class="text-xl font-bold text-accent">55 SEK</span>
4274                          </span>
4275                          
4276                          <!-- Original price -->
4277                  
4278                        </div>
4279                  
4280                        <!-- Add to basket -->
4281                        <div class="flex justify-between gap-2 px-6 mt-2">
4282                  
4283                            <!-- Buy button -->
4284                              
4285                              <!-- No options -->
4286                              <button @click="$store.cart.add({
4287                                          productID: 673,
4288                                          quantity: 1
4289                                        })"
4290                                aria-label="Lägg i korgen"
4291                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
4292                                
4293                                <!-- Icon (cart) -->
4294                                
4295                                <!-- Text -->
4296                                Lägg i korgen
4297                  
4298                              </button>
4299                  
4300                              <!-- Has options -->
4301                  
4302                              <!-- Sold out -->
4303                            
4304                  
4305                  
4306                            
4307                            <!-- Link to product when no buy button -->
4308                  
4309                            <!-- Wishlist -->
4310                            <div data-qb-selector="product-item-wishlist" data-product-id="673" class="flex items-center"></div>
4311                  
4312                        </div>
4313                        
4314                        <!-- Stock status -->
4315                        <div class="text-sm px-6 pt-4">
4316                  
4317                          <!-- Sold out -->
4318                  
4319                          <!-- Not sold out -->
4320                          <span data-in-stock-text="I lager"
4321                            data-out-of-stock-text=""
4322                            data-combination-not-exist-text="Variant finns ej">
4323                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
4324                          </span>
4325                  
4326                        </div>
4327                      
4328                      </div>
4329                    </div>
4330                  </div>
4331      
4332      
4333
4334
4335
4336                  
4337
4338                  
4339
4340
4341
4342                  
4343
4344                  
4345                  <div x-data="{ layoutSingle: '4' == '1' }"
4346                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
4347                  
4348                    data-qb-selector="product-item"
4349                    data-product-id="672"
4350                  
4351                    data-pid="672"
4352                    data-s-title="HOWND / Papaya Chia &amp; Lentil Dahl 375g"
4353                    data-s-price="55"
4354                    data-price="55"
4355                  >
4356                  
4357                    <!-- Image -->
4358                    <a class="block relative w-full " href="/hundmat/hownd-papaya-chia-lentil-dahl375g">
4359                      <div class="relative group overflow-hidden rounded-tnone">
4360                        <img loading="lazy" 
4361                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
4362                          ix-src="https://cdn.quickbutik.com/images/2666L/products/697a5ddca8468.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="HOWND / Papaya Chia &amp; Lentil Dahl 375g">
4363                        
4364                        <img loading="lazy"
4365                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
4366                          ix-src="https://cdn.quickbutik.com/images/2666L/products/697a5de71a731.webp" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="HOWND / Papaya Chia &amp; Lentil Dahl 375g">
4367                        
4368                      </div>
4369                    </a>
4370                  
4371                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
4372                      <a class="block px-6 mb-4" href="/hundmat/hownd-papaya-chia-lentil-dahl375g">
4373                        
4374                        <!-- Title -->
4375                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
4376                          HOWND / Papaya Chia &amp; Lentil Dahl 375g
4377                        </h3>
4378                  
4379                        <!-- Reviews container -->
4380                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
4381                  
4382                      </a>
4383                  
4384                      <!-- Description -->
4385                      <template x-if="layoutSingle">
4386                        
4387                      </template>
4388                  
4389                      <div>
4390                  
4391                        <!-- Price -->
4392                        <div class="px-6">
4393                  
4394                          <!-- Current price -->
4395                          <span>
4396                            <span class="text-xl font-bold text-accent">55 SEK</span>
4397                          </span>
4398                          
4399                          <!-- Original price -->
4400                  
4401                        </div>
4402                  
4403                        <!-- Add to basket -->
4404                        <div class="flex justify-between gap-2 px-6 mt-2">
4405                  
4406                            <!-- Buy button -->
4407                              
4408                              <!-- No options -->
4409                              <button @click="$store.cart.add({
4410                                          productID: 672,
4411                                          quantity: 1
4412                                        })"
4413                                aria-label="Lägg i korgen"
4414                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
4415                                
4416                                <!-- Icon (cart) -->
4417                                
4418                                <!-- Text -->
4419                                Lägg i korgen
4420                  
4421                              </button>
4422                  
4423                              <!-- Has options -->
4424                  
4425                              <!-- Sold out -->
4426                            
4427                  
4428                  
4429                            
4430                            <!-- Link to product when no buy button -->
4431                  
4432                            <!-- Wishlist -->
4433                            <div data-qb-selector="product-item-wishlist" data-product-id="672" class="flex items-center"></div>
4434                  
4435                        </div>
4436                        
4437                        <!-- Stock status -->
4438                        <div class="text-sm px-6 pt-4">
4439                  
4440                          <!-- Sold out -->
4441                  
4442                          <!-- Not sold out -->
4443                          <span data-in-stock-text="I lager"
4444                            data-out-of-stock-text=""
4445                            data-combination-not-exist-text="Variant finns ej">
4446                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
4447                          </span>
4448                  
4449                        </div>
4450                      
4451                      </div>
4452                    </div>
4453                  </div>
4454      
4455      
4456
4457
4458
4459                  
4460
4461                  
4462
4463
4464
4465                  
4466
4467                  
4468                  <div x-data="{ layoutSingle: '4' == '1' }"
4469                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
4470                  
4471                    data-qb-selector="product-item"
4472                    data-product-id="671"
4473                  
4474                    data-pid="671"
4475                    data-s-title="HOWND / Quinoa And Pumpkin Casserole 400g"
4476                    data-s-price="55"
4477                    data-price="55"
4478                  >
4479                  
4480                    <!-- Image -->
4481                    <a class="block relative w-full " href="/hundmat/hownd-quinoa-and-pumpkin-casserole">
4482                      <div class="relative group overflow-hidden rounded-tnone">
4483                        <img loading="lazy" 
4484                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
4485                          ix-src="https://cdn.quickbutik.com/images/2666L/products/61768f1382525.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="HOWND / Quinoa And Pumpkin Casserole 400g">
4486                        
4487                        <img loading="lazy"
4488                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
4489                          ix-src="https://cdn.quickbutik.com/images/2666L/products/657b6d9cd37ee.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="HOWND / Quinoa And Pumpkin Casserole 400g">
4490                        
4491                      </div>
4492                    </a>
4493                  
4494                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
4495                      <a class="block px-6 mb-4" href="/hundmat/hownd-quinoa-and-pumpkin-casserole">
4496                        
4497                        <!-- Title -->
4498                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
4499                          HOWND / Quinoa And Pumpkin Casserole 400g
4500                        </h3>
4501                  
4502                        <!-- Reviews container -->
4503                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
4504                  
4505                      </a>
4506                  
4507                      <!-- Description -->
4508                      <template x-if="layoutSingle">
4509                        
4510                      </template>
4511                  
4512                      <div>
4513                  
4514                        <!-- Price -->
4515                        <div class="px-6">
4516                  
4517                          <!-- Current price -->
4518                          <span>
4519                            <span class="text-xl font-bold text-accent">55 SEK</span>
4520                          </span>
4521                          
4522                          <!-- Original price -->
4523                  
4524                        </div>
4525                  
4526                        <!-- Add to basket -->
4527                        <div class="flex justify-between gap-2 px-6 mt-2">
4528                  
4529                            <!-- Buy button -->
4530                              
4531                              <!-- No options -->
4532                  
4533                              <!-- Has options -->
4534                  
4535                              <!-- Sold out -->
4536                              <a href="/hundmat/hownd-quinoa-and-pumpkin-casserole" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
4537                  
4538                                <!-- Icon -->
4539                  
4540                                <!-- Text -->
4541                                Läs mer
4542                  
4543                              </a>
4544                            
4545                  
4546                  
4547                            
4548                            <!-- Link to product when no buy button -->
4549                  
4550                            <!-- Wishlist -->
4551                            <div data-qb-selector="product-item-wishlist" data-product-id="671" class="flex items-center"></div>
4552                  
4553                        </div>
4554                        
4555                        <!-- Stock status -->
4556                        <div class="text-sm px-6 pt-4">
4557                  
4558                          <!-- Sold out -->
4559                          <span>
4560                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
4561                          </span>
4562                  
4563                          <!-- Not sold out -->
4564                  
4565                        </div>
4566                      
4567                      </div>
4568                    </div>
4569                  </div>
4570      
4571      
4572
4573
4574
4575                  
4576
4577                  
4578
4579
4580
4581                  
4582
4583                  
4584                  <div x-data="{ layoutSingle: '4' == '1' }"
4585                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
4586                  
4587                    data-qb-selector="product-item"
4588                    data-product-id="670"
4589                  
4590                    data-pid="670"
4591                    data-s-title="HOWND / Delicious Pumpkin Quinoa &amp; Moringa Superfood 2kg/10kg"
4592                    data-s-price="249.00"
4593                    data-price="249.00"
4594                  >
4595                  
4596                    <!-- Image -->
4597                    <a class="block relative w-full " href="/hundmat/hownd-plant-powered-superfood-with-hemp-protein-and-moringa">
4598                      <div class="relative group overflow-hidden rounded-tnone">
4599                        <img loading="lazy" 
4600                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
4601                          ix-src="https://cdn.quickbutik.com/images/2666L/products/660bca54cf49b.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="HOWND / Delicious Pumpkin Quinoa &amp; Moringa Superfood 2kg/10kg">
4602                        
4603                        <img loading="lazy"
4604                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
4605                          ix-src="https://cdn.quickbutik.com/images/2666L/products/660bca54db7e8.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="HOWND / Delicious Pumpkin Quinoa &amp; Moringa Superfood 2kg/10kg">
4606                        
4607                      </div>
4608                    </a>
4609                  
4610                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
4611                      <a class="block px-6 mb-4" href="/hundmat/hownd-plant-powered-superfood-with-hemp-protein-and-moringa">
4612                        
4613                        <!-- Title -->
4614                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
4615                          HOWND / Delicious Pumpkin Quinoa &amp; Moringa Superfood 2kg/10kg
4616                        </h3>
4617                  
4618                        <!-- Reviews container -->
4619                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
4620                  
4621                      </a>
4622                  
4623                      <!-- Description -->
4624                      <template x-if="layoutSingle">
4625                        
4626                      </template>
4627                  
4628                      <div>
4629                  
4630                        <!-- Price -->
4631                        <div class="px-6">
4632                  
4633                          <!-- Current price -->
4634                          <span>
4635                            <span class="text-xl font-bold text-accent">249 SEK</span>
4636                          </span>
4637                          
4638                          <!-- Original price -->
4639                  
4640                        </div>
4641                  
4642                        <!-- Add to basket -->
4643                        <div class="flex justify-between gap-2 px-6 mt-2">
4644                  
4645                            <!-- Buy button -->
4646                              
4647                              <!-- No options -->
4648                  
4649                              <!-- Has options -->
4650                  
4651                              <!-- Sold out -->
4652                              <a href="/hundmat/hownd-plant-powered-superfood-with-hemp-protein-and-moringa" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
4653                  
4654                                <!-- Icon -->
4655                  
4656                                <!-- Text -->
4657                                Läs mer
4658                  
4659                              </a>
4660                            
4661                  
4662                  
4663                            
4664                            <!-- Link to product when no buy button -->
4665                  
4666                            <!-- Wishlist -->
4667                            <div data-qb-selector="product-item-wishlist" data-product-id="670" class="flex items-center"></div>
4668                  
4669                        </div>
4670                        
4671                        <!-- Stock status -->
4672                        <div class="text-sm px-6 pt-4">
4673                  
4674                          <!-- Sold out -->
4675                          <span>
4676                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
4677                          </span>
4678                  
4679                          <!-- Not sold out -->
4680                  
4681                        </div>
4682                      
4683                      </div>
4684                    </div>
4685                  </div>
4686      
4687      
4688
4689
4690
4691                  
4692
4693                  
4694
4695
4696
4697                  
4698
4699                  
4700                  <div x-data="{ layoutSingle: '4' == '1' }"
4701                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
4702                  
4703                    data-qb-selector="product-item"
4704                    data-product-id="665"
4705                  
4706                    data-pid="665"
4707                    data-s-title="VEGDOG Vuxen Ärtor &amp; Havregryn i Tetra Pak"
4708                    data-s-price="39.00"
4709                    data-price="39.00"
4710                  >
4711                  
4712                    <!-- Image -->
4713                    <a class="block relative w-full " href="/hundmat/vegdog-adult-200g">
4714                      <div class="relative group overflow-hidden rounded-tnone">
4715                        <img loading="lazy" 
4716                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
4717                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a15c113b13.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Vuxen Ärtor &amp; Havregryn i Tetra Pak">
4718                        
4719                        <img loading="lazy"
4720                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
4721                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a15c147329.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Vuxen Ärtor &amp; Havregryn i Tetra Pak">
4722                        
4723                      </div>
4724                    </a>
4725                  
4726                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
4727                      <a class="block px-6 mb-4" href="/hundmat/vegdog-adult-200g">
4728                        
4729                        <!-- Title -->
4730                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
4731                          VEGDOG Vuxen Ärtor &amp; Havregryn i Tetra Pak
4732                        </h3>
4733                  
4734                        <!-- Reviews container -->
4735                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
4736                  
4737                      </a>
4738                  
4739                      <!-- Description -->
4740                      <template x-if="layoutSingle">
4741                        
4742                      </template>
4743                  
4744                      <div>
4745                  
4746                        <!-- Price -->
4747                        <div class="px-6">
4748                  
4749                          <!-- Current price -->
4750                          <span>
4751                            <span class="text-xl font-bold text-accent">39 SEK</span>
4752                          </span>
4753                          
4754                          <!-- Original price -->
4755                  
4756                        </div>
4757                  
4758                        <!-- Add to basket -->
4759                        <div class="flex justify-between gap-2 px-6 mt-2">
4760                  
4761                            <!-- Buy button -->
4762                              
4763                              <!-- No options -->
4764                  
4765                              <!-- Has options -->
4766                              <a href="/hundmat/vegdog-adult-200g" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
4767                  
4768                                <!-- Icon (arrow) -->
4769                  
4770                                <!-- Text -->
4771                                Läs mer
4772                  
4773                              </a>
4774                  
4775                              <!-- Sold out -->
4776                            
4777                  
4778                  
4779                            
4780                            <!-- Link to product when no buy button -->
4781                  
4782                            <!-- Wishlist -->
4783                            <div data-qb-selector="product-item-wishlist" data-product-id="665" class="flex items-center"></div>
4784                  
4785                        </div>
4786                        
4787                        <!-- Stock status -->
4788                        <div class="text-sm px-6 pt-4">
4789                  
4790                          <!-- Sold out -->
4791                  
4792                          <!-- Not sold out -->
4793                          <span data-in-stock-text="I lager"
4794                            data-out-of-stock-text=""
4795                            data-combination-not-exist-text="Variant finns ej">
4796                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
4797                          </span>
4798                  
4799                        </div>
4800                      
4801                      </div>
4802                    </div>
4803                  </div>
4804      
4805      
4806
4807
4808
4809                  
4810
4811                  
4812
4813
4814
4815                  
4816
4817                  
4818                  <div x-data="{ layoutSingle: '4' == '1' }"
4819                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
4820                  
4821                    data-qb-selector="product-item"
4822                    data-product-id="643"
4823                  
4824                    data-pid="643"
4825                    data-s-title="AMI DOG Wet ORANGE 400g"
4826                    data-s-price="45"
4827                    data-price="45"
4828                  >
4829                  
4830                    <!-- Image -->
4831                    <a class="block relative w-full " href="/hundmat/ami-dog-wet-orange-400g">
4832                      <div class="relative group overflow-hidden rounded-tnone">
4833                        <img loading="lazy" 
4834                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
4835                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6544be2090f04.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet ORANGE 400g">
4836                        
4837                        <img loading="lazy"
4838                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
4839                          ix-src="https://cdn.quickbutik.com/images/2666L/products/657b0d4ae16d7.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet ORANGE 400g">
4840                        
4841                      </div>
4842                    </a>
4843                  
4844                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
4845                      <a class="block px-6 mb-4" href="/hundmat/ami-dog-wet-orange-400g">
4846                        
4847                        <!-- Title -->
4848                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
4849                          AMI DOG Wet ORANGE 400g
4850                        </h3>
4851                  
4852                        <!-- Reviews container -->
4853                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
4854                  
4855                      </a>
4856                  
4857                      <!-- Description -->
4858                      <template x-if="layoutSingle">
4859                        
4860                      </template>
4861                  
4862                      <div>
4863                  
4864                        <!-- Price -->
4865                        <div class="px-6">
4866                  
4867                          <!-- Current price -->
4868                          <span>
4869                            <span class="text-xl font-bold text-accent">45 SEK</span>
4870                          </span>
4871                          
4872                          <!-- Original price -->
4873                  
4874                        </div>
4875                  
4876                        <!-- Add to basket -->
4877                        <div class="flex justify-between gap-2 px-6 mt-2">
4878                  
4879                            <!-- Buy button -->
4880                              
4881                              <!-- No options -->
4882                              <button @click="$store.cart.add({
4883                                          productID: 643,
4884                                          quantity: 1
4885                                        })"
4886                                aria-label="Lägg i korgen"
4887                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
4888                                
4889                                <!-- Icon (cart) -->
4890                                
4891                                <!-- Text -->
4892                                Lägg i korgen
4893                  
4894                              </button>
4895                  
4896                              <!-- Has options -->
4897                  
4898                              <!-- Sold out -->
4899                            
4900                  
4901                  
4902                            
4903                            <!-- Link to product when no buy button -->
4904                  
4905                            <!-- Wishlist -->
4906                            <div data-qb-selector="product-item-wishlist" data-product-id="643" class="flex items-center"></div>
4907                  
4908                        </div>
4909                        
4910                        <!-- Stock status -->
4911                        <div class="text-sm px-6 pt-4">
4912                  
4913                          <!-- Sold out -->
4914                  
4915                          <!-- Not sold out -->
4916                          <span data-in-stock-text="I lager"
4917                            data-out-of-stock-text=""
4918                            data-combination-not-exist-text="Variant finns ej">
4919                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
4920                          </span>
4921                  
4922                        </div>
4923                      
4924                      </div>
4925                    </div>
4926                  </div>
4927      
4928      
4929
4930
4931
4932                  
4933
4934                  
4935
4936
4937
4938                  
4939
4940                  
4941                  <div x-data="{ layoutSingle: '4' == '1' }"
4942                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
4943                  
4944                    data-qb-selector="product-item"
4945                    data-product-id="633"
4946                  
4947                    data-pid="633"
4948                    data-s-title="AMI DOG Mini &amp; Toys 1kg/3kg"
4949                    data-s-price="159.00"
4950                    data-price="159.00"
4951                  >
4952                  
4953                    <!-- Image -->
4954                    <a class="block relative w-full " href="/hundmat/ami-dog-small-size">
4955                      <div class="relative group overflow-hidden rounded-tnone">
4956                        <img loading="lazy" 
4957                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
4958                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66b0633573cf0.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Mini &amp; Toys 1kg/3kg">
4959                        
4960                        <img loading="lazy"
4961                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
4962                          ix-src="https://cdn.quickbutik.com/images/2666L/products/67d9390121029.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Mini &amp; Toys 1kg/3kg">
4963                        
4964                      </div>
4965                    </a>
4966                  
4967                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
4968                      <a class="block px-6 mb-4" href="/hundmat/ami-dog-small-size">
4969                        
4970                        <!-- Title -->
4971                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
4972                          AMI DOG Mini &amp; Toys 1kg/3kg
4973                        </h3>
4974                  
4975                        <!-- Reviews container -->
4976                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
4977                  
4978                      </a>
4979                  
4980                      <!-- Description -->
4981                      <template x-if="layoutSingle">
4982                        
4983                      </template>
4984                  
4985                      <div>
4986                  
4987                        <!-- Price -->
4988                        <div class="px-6">
4989                  
4990                          <!-- Current price -->
4991                          <span>
4992                            <span class="text-xl font-bold text-accent">159 SEK</span>
4993                          </span>
4994                          
4995                          <!-- Original price -->
4996                  
4997                        </div>
4998                  
4999                        <!-- Add to basket -->
5000                        <div class="flex justify-between gap-2 px-6 mt-2">
5001                  
5002                            <!-- Buy button -->
5003                              
5004                              <!-- No options -->
5005                  
5006                              <!-- Has options -->
5007                              <a href="/hundmat/ami-dog-small-size" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
5008                  
5009                                <!-- Icon (arrow) -->
5010                  
5011                                <!-- Text -->
5012                                Läs mer
5013                  
5014                              </a>
5015                  
5016                              <!-- Sold out -->
5017                            
5018                  
5019                  
5020                            
5021                            <!-- Link to product when no buy button -->
5022                  
5023                            <!-- Wishlist -->
5024                            <div data-qb-selector="product-item-wishlist" data-product-id="633" class="flex items-center"></div>
5025                  
5026                        </div>
5027                        
5028                        <!-- Stock status -->
5029                        <div class="text-sm px-6 pt-4">
5030                  
5031                          <!-- Sold out -->
5032                  
5033                          <!-- Not sold out -->
5034                          <span data-in-stock-text="I lager"
5035                            data-out-of-stock-text=""
5036                            data-combination-not-exist-text="Variant finns ej">
5037                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
5038                          </span>
5039                  
5040                        </div>
5041                      
5042                      </div>
5043                    </div>
5044                  </div>
5045      
5046      
5047
5048
5049
5050                  
5051
5052                  
5053
5054
5055
5056                  
5057
5058                  
5059                  <div x-data="{ layoutSingle: '4' == '1' }"
5060                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
5061                  
5062                    data-qb-selector="product-item"
5063                    data-product-id="629"
5064                  
5065                    data-pid="629"
5066                    data-s-title="Vegan4Dogs Pauline 400g/800g"
5067                    data-s-price="59.00"
5068                    data-price="59.00"
5069                  >
5070                  
5071                    <!-- Image -->
5072                    <a class="block relative w-full " href="/hundmat/vegan4dogs-pauline-400g800g">
5073                      <div class="relative group overflow-hidden rounded-tnone">
5074                        <img loading="lazy" 
5075                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
5076                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65bbb710d2481.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Vegan4Dogs Pauline 400g/800g">
5077                        
5078                        <img loading="lazy"
5079                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
5080                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65bbb71152d6b.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Vegan4Dogs Pauline 400g/800g">
5081                        
5082                      </div>
5083                    </a>
5084                  
5085                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
5086                      <a class="block px-6 mb-4" href="/hundmat/vegan4dogs-pauline-400g800g">
5087                        
5088                        <!-- Title -->
5089                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
5090                          Vegan4Dogs Pauline 400g/800g
5091                        </h3>
5092                  
5093                        <!-- Reviews container -->
5094                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
5095                  
5096                      </a>
5097                  
5098                      <!-- Description -->
5099                      <template x-if="layoutSingle">
5100                        
5101                      </template>
5102                  
5103                      <div>
5104                  
5105                        <!-- Price -->
5106                        <div class="px-6">
5107                  
5108                          <!-- Current price -->
5109                          <span>
5110                            <span class="text-xl font-bold text-accent">59 SEK</span>
5111                          </span>
5112                          
5113                          <!-- Original price -->
5114                  
5115                        </div>
5116                  
5117                        <!-- Add to basket -->
5118                        <div class="flex justify-between gap-2 px-6 mt-2">
5119                  
5120                            <!-- Buy button -->
5121                              
5122                              <!-- No options -->
5123                  
5124                              <!-- Has options -->
5125                  
5126                              <!-- Sold out -->
5127                              <a href="/hundmat/vegan4dogs-pauline-400g800g" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
5128                  
5129                                <!-- Icon -->
5130                  
5131                                <!-- Text -->
5132                                Läs mer
5133                  
5134                              </a>
5135                            
5136                  
5137                  
5138                            
5139                            <!-- Link to product when no buy button -->
5140                  
5141                            <!-- Wishlist -->
5142                            <div data-qb-selector="product-item-wishlist" data-product-id="629" class="flex items-center"></div>
5143                  
5144                        </div>
5145                        
5146                        <!-- Stock status -->
5147                        <div class="text-sm px-6 pt-4">
5148                  
5149                          <!-- Sold out -->
5150                          <span>
5151                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
5152                          </span>
5153                  
5154                          <!-- Not sold out -->
5155                  
5156                        </div>
5157                      
5158                      </div>
5159                    </div>
5160                  </div>
5161      
5162      
5163
5164
5165
5166                  
5167
5168                  
5169
5170
5171
5172                  
5173
5174                  
5175                  <div x-data="{ layoutSingle: '4' == '1' }"
5176                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
5177                  
5178                    data-qb-selector="product-item"
5179                    data-product-id="619"
5180                  
5181                    data-pid="619"
5182                    data-s-title="VEGDOG Green Crunch 1kg/4kg/10kg"
5183                    data-s-price="149.00"
5184                    data-price="149.00"
5185                  >
5186                  
5187                    <!-- Image -->
5188                    <a class="block relative w-full " href="/hundmat/vegdog-green-crunch">
5189                      <div class="relative group overflow-hidden rounded-tnone">
5190                        <img loading="lazy" 
5191                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
5192                          ix-src="https://cdn.quickbutik.com/images/2666L/products/69e9cb262c830.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Green Crunch 1kg/4kg/10kg">
5193                        
5194                        <img loading="lazy"
5195                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
5196                          ix-src="https://cdn.quickbutik.com/images/2666L/products/69e9cb1794510.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Green Crunch 1kg/4kg/10kg">
5197                        
5198                      </div>
5199                    </a>
5200                  
5201                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
5202                      <a class="block px-6 mb-4" href="/hundmat/vegdog-green-crunch">
5203                        
5204                        <!-- Title -->
5205                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
5206                          VEGDOG Green Crunch 1kg/4kg/10kg
5207                        </h3>
5208                  
5209                        <!-- Reviews container -->
5210                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
5211                  
5212                      </a>
5213                  
5214                      <!-- Description -->
5215                      <template x-if="layoutSingle">
5216                        
5217                      </template>
5218                  
5219                      <div>
5220                  
5221                        <!-- Price -->
5222                        <div class="px-6">
5223                  
5224                          <!-- Current price -->
5225                          <span>
5226                            <span class="text-xl font-bold text-accent">149 SEK</span>
5227                          </span>
5228                          
5229                          <!-- Original price -->
5230                  
5231                        </div>
5232                  
5233                        <!-- Add to basket -->
5234                        <div class="flex justify-between gap-2 px-6 mt-2">
5235                  
5236                            <!-- Buy button -->
5237                              
5238                              <!-- No options -->
5239                  
5240                              <!-- Has options -->
5241                              <a href="/hundmat/vegdog-green-crunch" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
5242                  
5243                                <!-- Icon (arrow) -->
5244                  
5245                                <!-- Text -->
5246                                Läs mer
5247                  
5248                              </a>
5249                  
5250                              <!-- Sold out -->
5251                            
5252                  
5253                  
5254                            
5255                            <!-- Link to product when no buy button -->
5256                  
5257                            <!-- Wishlist -->
5258                            <div data-qb-selector="product-item-wishlist" data-product-id="619" class="flex items-center"></div>
5259                  
5260                        </div>
5261                        
5262                        <!-- Stock status -->
5263                        <div class="text-sm px-6 pt-4">
5264                  
5265                          <!-- Sold out -->
5266                  
5267                          <!-- Not sold out -->
5268                          <span data-in-stock-text="I lager"
5269                            data-out-of-stock-text=""
5270                            data-combination-not-exist-text="Variant finns ej">
5271                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
5272                          </span>
5273                  
5274                        </div>
5275                      
5276                      </div>
5277                    </div>
5278                  </div>
5279      
5280      
5281
5282
5283
5284                  
5285
5286                  
5287
5288
5289
5290                  
5291
5292                  
5293                  <div x-data="{ layoutSingle: '4' == '1' }"
5294                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
5295                  
5296                    data-qb-selector="product-item"
5297                    data-product-id="362"
5298                  
5299                    data-pid="362"
5300                    data-s-title="AMI DOG Wet GREEN 400g"
5301                    data-s-price="45"
5302                    data-price="45"
5303                  >
5304                  
5305                    <!-- Image -->
5306                    <a class="block relative w-full " href="/hundmat/ami-dog-wet-green-400g">
5307                      <div class="relative group overflow-hidden rounded-tnone">
5308                        <img loading="lazy" 
5309                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
5310                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6544bdb8de3d9.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet GREEN 400g">
5311                        
5312                        <img loading="lazy"
5313                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
5314                          ix-src="https://cdn.quickbutik.com/images/2666L/products/657b09f691c89.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet GREEN 400g">
5315                        
5316                      </div>
5317                    </a>
5318                  
5319                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
5320                      <a class="block px-6 mb-4" href="/hundmat/ami-dog-wet-green-400g">
5321                        
5322                        <!-- Title -->
5323                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
5324                          AMI DOG Wet GREEN 400g
5325                        </h3>
5326                  
5327                        <!-- Reviews container -->
5328                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
5329                  
5330                      </a>
5331                  
5332                      <!-- Description -->
5333                      <template x-if="layoutSingle">
5334                        
5335                      </template>
5336                  
5337                      <div>
5338                  
5339                        <!-- Price -->
5340                        <div class="px-6">
5341                  
5342                          <!-- Current price -->
5343                          <span>
5344                            <span class="text-xl font-bold text-accent">45 SEK</span>
5345                          </span>
5346                          
5347                          <!-- Original price -->
5348                  
5349                        </div>
5350                  
5351                        <!-- Add to basket -->
5352                        <div class="flex justify-between gap-2 px-6 mt-2">
5353                  
5354                            <!-- Buy button -->
5355                              
5356                              <!-- No options -->
5357                              <button @click="$store.cart.add({
5358                                          productID: 362,
5359                                          quantity: 1
5360                                        })"
5361                                aria-label="Lägg i korgen"
5362                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
5363                                
5364                                <!-- Icon (cart) -->
5365                                
5366                                <!-- Text -->
5367                                Lägg i korgen
5368                  
5369                              </button>
5370                  
5371                              <!-- Has options -->
5372                  
5373                              <!-- Sold out -->
5374                            
5375                  
5376                  
5377                            
5378                            <!-- Link to product when no buy button -->
5379                  
5380                            <!-- Wishlist -->
5381                            <div data-qb-selector="product-item-wishlist" data-product-id="362" class="flex items-center"></div>
5382                  
5383                        </div>
5384                        
5385                        <!-- Stock status -->
5386                        <div class="text-sm px-6 pt-4">
5387                  
5388                          <!-- Sold out -->
5389                  
5390                          <!-- Not sold out -->
5391                          <span data-in-stock-text="I lager"
5392                            data-out-of-stock-text=""
5393                            data-combination-not-exist-text="Variant finns ej">
5394                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
5395                          </span>
5396                  
5397                        </div>
5398                      
5399                      </div>
5400                    </div>
5401                  </div>
5402      
5403      
5404
5405
5406
5407                  
5408
5409                  
5410
5411
5412
5413                  
5414
5415                  
5416                  <div x-data="{ layoutSingle: '4' == '1' }"
5417                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
5418                  
5419                    data-qb-selector="product-item"
5420                    data-product-id="361"
5421                  
5422                    data-pid="361"
5423                    data-s-title="AMI DOG Wet RED 400g"
5424                    data-s-price="45"
5425                    data-price="45"
5426                  >
5427                  
5428                    <!-- Image -->
5429                    <a class="block relative w-full " href="/hundmat/ami-dog-wet-red-400g">
5430                      <div class="relative group overflow-hidden rounded-tnone">
5431                        <img loading="lazy" 
5432                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
5433                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6544be4dd4e31.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet RED 400g">
5434                        
5435                        <img loading="lazy"
5436                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
5437                          ix-src="https://cdn.quickbutik.com/images/2666L/products/657b0d9a8ee46.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet RED 400g">
5438                        
5439                      </div>
5440                    </a>
5441                  
5442                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
5443                      <a class="block px-6 mb-4" href="/hundmat/ami-dog-wet-red-400g">
5444                        
5445                        <!-- Title -->
5446                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
5447                          AMI DOG Wet RED 400g
5448                        </h3>
5449                  
5450                        <!-- Reviews container -->
5451                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
5452                  
5453                      </a>
5454                  
5455                      <!-- Description -->
5456                      <template x-if="layoutSingle">
5457                        
5458                      </template>
5459                  
5460                      <div>
5461                  
5462                        <!-- Price -->
5463                        <div class="px-6">
5464                  
5465                          <!-- Current price -->
5466                          <span>
5467                            <span class="text-xl font-bold text-accent">45 SEK</span>
5468                          </span>
5469                          
5470                          <!-- Original price -->
5471                  
5472                        </div>
5473                  
5474                        <!-- Add to basket -->
5475                        <div class="flex justify-between gap-2 px-6 mt-2">
5476                  
5477                            <!-- Buy button -->
5478                              
5479                              <!-- No options -->
5480                              <button @click="$store.cart.add({
5481                                          productID: 361,
5482                                          quantity: 1
5483                                        })"
5484                                aria-label="Lägg i korgen"
5485                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
5486                                
5487                                <!-- Icon (cart) -->
5488                                
5489                                <!-- Text -->
5490                                Lägg i korgen
5491                  
5492                              </button>
5493                  
5494                              <!-- Has options -->
5495                  
5496                              <!-- Sold out -->
5497                            
5498                  
5499                  
5500                            
5501                            <!-- Link to product when no buy button -->
5502                  
5503                            <!-- Wishlist -->
5504                            <div data-qb-selector="product-item-wishlist" data-product-id="361" class="flex items-center"></div>
5505                  
5506                        </div>
5507                        
5508                        <!-- Stock status -->
5509                        <div class="text-sm px-6 pt-4">
5510                  
5511                          <!-- Sold out -->
5512                  
5513                          <!-- Not sold out -->
5514                          <span data-in-stock-text="I lager"
5515                            data-out-of-stock-text=""
5516                            data-combination-not-exist-text="Variant finns ej">
5517                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
5518                          </span>
5519                  
5520                        </div>
5521                      
5522                      </div>
5523                    </div>
5524                  </div>
5525      
5526      
5527
5528
5529
5530                  
5531
5532                  
5533
5534
5535
5536                  
5537
5538                  
5539                  <div x-data="{ layoutSingle: '4' == '1' }"
5540                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
5541                  
5542                    data-qb-selector="product-item"
5543                    data-product-id="360"
5544                  
5545                    data-pid="360"
5546                    data-s-title="AMI DOG Wet YELLOW 400g"
5547                    data-s-price="45"
5548                    data-price="45"
5549                  >
5550                  
5551                    <!-- Image -->
5552                    <a class="block relative w-full " href="/hundmat/ami-dog-wet-yellow-400g">
5553                      <div class="relative group overflow-hidden rounded-tnone">
5554                        <img loading="lazy" 
5555                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
5556                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6544be77c72ac.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet YELLOW 400g">
5557                        
5558                        <img loading="lazy"
5559                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
5560                          ix-src="https://cdn.quickbutik.com/images/2666L/products/657b0eaf0d389.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Wet YELLOW 400g">
5561                        
5562                      </div>
5563                    </a>
5564                  
5565                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
5566                      <a class="block px-6 mb-4" href="/hundmat/ami-dog-wet-yellow-400g">
5567                        
5568                        <!-- Title -->
5569                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
5570                          AMI DOG Wet YELLOW 400g
5571                        </h3>
5572                  
5573                        <!-- Reviews container -->
5574                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
5575                  
5576                      </a>
5577                  
5578                      <!-- Description -->
5579                      <template x-if="layoutSingle">
5580                        
5581                      </template>
5582                  
5583                      <div>
5584                  
5585                        <!-- Price -->
5586                        <div class="px-6">
5587                  
5588                          <!-- Current price -->
5589                          <span>
5590                            <span class="text-xl font-bold text-accent">45 SEK</span>
5591                          </span>
5592                          
5593                          <!-- Original price -->
5594                  
5595                        </div>
5596                  
5597                        <!-- Add to basket -->
5598                        <div class="flex justify-between gap-2 px-6 mt-2">
5599                  
5600                            <!-- Buy button -->
5601                              
5602                              <!-- No options -->
5603                              <button @click="$store.cart.add({
5604                                          productID: 360,
5605                                          quantity: 1
5606                                        })"
5607                                aria-label="Lägg i korgen"
5608                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
5609                                
5610                                <!-- Icon (cart) -->
5611                                
5612                                <!-- Text -->
5613                                Lägg i korgen
5614                  
5615                              </button>
5616                  
5617                              <!-- Has options -->
5618                  
5619                              <!-- Sold out -->
5620                            
5621                  
5622                  
5623                            
5624                            <!-- Link to product when no buy button -->
5625                  
5626                            <!-- Wishlist -->
5627                            <div data-qb-selector="product-item-wishlist" data-product-id="360" class="flex items-center"></div>
5628                  
5629                        </div>
5630                        
5631                        <!-- Stock status -->
5632                        <div class="text-sm px-6 pt-4">
5633                  
5634                          <!-- Sold out -->
5635                  
5636                          <!-- Not sold out -->
5637                          <span data-in-stock-text="I lager"
5638                            data-out-of-stock-text=""
5639                            data-combination-not-exist-text="Variant finns ej">
5640                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
5641                          </span>
5642                  
5643                        </div>
5644                      
5645                      </div>
5646                    </div>
5647                  </div>
5648      
5649      
5650
5651
5652
5653                  
5654
5655                  
5656
5657
5658
5659                  
5660
5661                  
5662                  <div x-data="{ layoutSingle: '4' == '1' }"
5663                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
5664                  
5665                    data-qb-selector="product-item"
5666                    data-product-id="134"
5667                  
5668                    data-pid="134"
5669                    data-s-title="Vegan4Dogs Greta Mini 2kg/10kg"
5670                    data-s-price="259.00"
5671                    data-price="259.00"
5672                  >
5673                  
5674                    <!-- Image -->
5675                    <a class="block relative w-full " href="/hundmat/vegan4dogs-greta-mini">
5676                      <div class="relative group overflow-hidden rounded-tnone">
5677                        <img loading="lazy" 
5678                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
5679                          ix-src="https://cdn.quickbutik.com/images/2666L/products/5ba4db4e69a98.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Vegan4Dogs Greta Mini 2kg/10kg">
5680                        
5681                        <img loading="lazy"
5682                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
5683                          ix-src="https://cdn.quickbutik.com/images/2666L/products/6542a52240e5e.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Vegan4Dogs Greta Mini 2kg/10kg">
5684                        
5685                      </div>
5686                    </a>
5687                  
5688                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
5689                      <a class="block px-6 mb-4" href="/hundmat/vegan4dogs-greta-mini">
5690                        
5691                        <!-- Title -->
5692                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
5693                          Vegan4Dogs Greta Mini 2kg/10kg
5694                        </h3>
5695                  
5696                        <!-- Reviews container -->
5697                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
5698                  
5699                      </a>
5700                  
5701                      <!-- Description -->
5702                      <template x-if="layoutSingle">
5703                        
5704                      </template>
5705                  
5706                      <div>
5707                  
5708                        <!-- Price -->
5709                        <div class="px-6">
5710                  
5711                          <!-- Current price -->
5712                          <span>
5713                            <span class="text-xl font-bold text-accent">259 SEK</span>
5714                          </span>
5715                          
5716                          <!-- Original price -->
5717                  
5718                        </div>
5719                  
5720                        <!-- Add to basket -->
5721                        <div class="flex justify-between gap-2 px-6 mt-2">
5722                  
5723                            <!-- Buy button -->
5724                              
5725                              <!-- No options -->
5726                  
5727                              <!-- Has options -->
5728                  
5729                              <!-- Sold out -->
5730                              <a href="/hundmat/vegan4dogs-greta-mini" area-label="Ej i lager" class="block  w-full button-py px-2 bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
5731                  
5732                                <!-- Icon -->
5733                  
5734                                <!-- Text -->
5735                                Läs mer
5736                  
5737                              </a>
5738                            
5739                  
5740                  
5741                            
5742                            <!-- Link to product when no buy button -->
5743                  
5744                            <!-- Wishlist -->
5745                            <div data-qb-selector="product-item-wishlist" data-product-id="134" class="flex items-center"></div>
5746                  
5747                        </div>
5748                        
5749                        <!-- Stock status -->
5750                        <div class="text-sm px-6 pt-4">
5751                  
5752                          <!-- Sold out -->
5753                          <span>
5754                            <span class="inline-block bg-red-600 rounded-full h-2 w-2 mr-1"></span> Ej i lager
5755                          </span>
5756                  
5757                          <!-- Not sold out -->
5758                  
5759                        </div>
5760                      
5761                      </div>
5762                    </div>
5763                  </div>
5764      
5765      
5766
5767
5768
5769                  
5770
5771                  
5772
5773
5774
5775                  
5776
5777                  
5778                  <div x-data="{ layoutSingle: '4' == '1' }"
5779                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
5780                  
5781                    data-qb-selector="product-item"
5782                    data-product-id="57"
5783                  
5784                    data-pid="57"
5785                    data-s-title="VEGDOG Sensitive lupinprotein 400g/800g"
5786                    data-s-price="45.00"
5787                    data-price="45.00"
5788                  >
5789                  
5790                    <!-- Image -->
5791                    <a class="block relative w-full " href="/hundmat/vegdog-sensitive-lupinprotein-400g800g">
5792                      <div class="relative group overflow-hidden rounded-tnone">
5793                        <img loading="lazy" 
5794                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
5795                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a091ea626d.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Sensitive lupinprotein 400g/800g">
5796                        
5797                        <img loading="lazy"
5798                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
5799                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a091ead7ce.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Sensitive lupinprotein 400g/800g">
5800                        
5801                      </div>
5802                    </a>
5803                  
5804                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
5805                      <a class="block px-6 mb-4" href="/hundmat/vegdog-sensitive-lupinprotein-400g800g">
5806                        
5807                        <!-- Title -->
5808                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
5809                          VEGDOG Sensitive lupinprotein 400g/800g
5810                        </h3>
5811                  
5812                        <!-- Reviews container -->
5813                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
5814                  
5815                      </a>
5816                  
5817                      <!-- Description -->
5818                      <template x-if="layoutSingle">
5819                        
5820                      </template>
5821                  
5822                      <div>
5823                  
5824                        <!-- Price -->
5825                        <div class="px-6">
5826                  
5827                          <!-- Current price -->
5828                          <span>
5829                            <span class="text-xl font-bold text-accent">45 SEK</span>
5830                          </span>
5831                          
5832                          <!-- Original price -->
5833                  
5834                        </div>
5835                  
5836                        <!-- Add to basket -->
5837                        <div class="flex justify-between gap-2 px-6 mt-2">
5838                  
5839                            <!-- Buy button -->
5840                              
5841                              <!-- No options -->
5842                  
5843                              <!-- Has options -->
5844                              <a href="/hundmat/vegdog-sensitive-lupinprotein-400g800g" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
5845                  
5846                                <!-- Icon (arrow) -->
5847                  
5848                                <!-- Text -->
5849                                Läs mer
5850                  
5851                              </a>
5852                  
5853                              <!-- Sold out -->
5854                            
5855                  
5856                  
5857                            
5858                            <!-- Link to product when no buy button -->
5859                  
5860                            <!-- Wishlist -->
5861                            <div data-qb-selector="product-item-wishlist" data-product-id="57" class="flex items-center"></div>
5862                  
5863                        </div>
5864                        
5865                        <!-- Stock status -->
5866                        <div class="text-sm px-6 pt-4">
5867                  
5868                          <!-- Sold out -->
5869                  
5870                          <!-- Not sold out -->
5871                          <span data-in-stock-text="I lager"
5872                            data-out-of-stock-text=""
5873                            data-combination-not-exist-text="Variant finns ej">
5874                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
5875                          </span>
5876                  
5877                        </div>
5878                      
5879                      </div>
5880                    </div>
5881                  </div>
5882      
5883      
5884
5885
5886
5887                  
5888
5889                  
5890
5891
5892
5893                  
5894
5895                  
5896                  <div x-data="{ layoutSingle: '4' == '1' }"
5897                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
5898                  
5899                    data-qb-selector="product-item"
5900                    data-product-id="56"
5901                  
5902                    data-pid="56"
5903                    data-s-title="VEGDOG Senior 400g/800g"
5904                    data-s-price="45.00"
5905                    data-price="45.00"
5906                  >
5907                  
5908                    <!-- Image -->
5909                    <a class="block relative w-full " href="/hundmat/vegdog-senior-400g">
5910                      <div class="relative group overflow-hidden rounded-tnone">
5911                        <img loading="lazy" 
5912                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
5913                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a0e6b14f6d.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Senior 400g/800g">
5914                        
5915                        <img loading="lazy"
5916                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
5917                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a0e6b4a727.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Senior 400g/800g">
5918                        
5919                      </div>
5920                    </a>
5921                  
5922                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
5923                      <a class="block px-6 mb-4" href="/hundmat/vegdog-senior-400g">
5924                        
5925                        <!-- Title -->
5926                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
5927                          VEGDOG Senior 400g/800g
5928                        </h3>
5929                  
5930                        <!-- Reviews container -->
5931                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
5932                  
5933                      </a>
5934                  
5935                      <!-- Description -->
5936                      <template x-if="layoutSingle">
5937                        
5938                      </template>
5939                  
5940                      <div>
5941                  
5942                        <!-- Price -->
5943                        <div class="px-6">
5944                  
5945                          <!-- Current price -->
5946                          <span>
5947                            <span class="text-xl font-bold text-accent">45 SEK</span>
5948                          </span>
5949                          
5950                          <!-- Original price -->
5951                  
5952                        </div>
5953                  
5954                        <!-- Add to basket -->
5955                        <div class="flex justify-between gap-2 px-6 mt-2">
5956                  
5957                            <!-- Buy button -->
5958                              
5959                              <!-- No options -->
5960                  
5961                              <!-- Has options -->
5962                              <a href="/hundmat/vegdog-senior-400g" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
5963                  
5964                                <!-- Icon (arrow) -->
5965                  
5966                                <!-- Text -->
5967                                Läs mer
5968                  
5969                              </a>
5970                  
5971                              <!-- Sold out -->
5972                            
5973                  
5974                  
5975                            
5976                            <!-- Link to product when no buy button -->
5977                  
5978                            <!-- Wishlist -->
5979                            <div data-qb-selector="product-item-wishlist" data-product-id="56" class="flex items-center"></div>
5980                  
5981                        </div>
5982                        
5983                        <!-- Stock status -->
5984                        <div class="text-sm px-6 pt-4">
5985                  
5986                          <!-- Sold out -->
5987                  
5988                          <!-- Not sold out -->
5989                          <span data-in-stock-text="I lager"
5990                            data-out-of-stock-text=""
5991                            data-combination-not-exist-text="Variant finns ej">
5992                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
5993                          </span>
5994                  
5995                        </div>
5996                      
5997                      </div>
5998                    </div>
5999                  </div>
6000      
6001      
6002
6003
6004
6005                  
6006
6007                  
6008
6009
6010
6011                  
6012
6013                  
6014                  <div x-data="{ layoutSingle: '4' == '1' }"
6015                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
6016                  
6017                    data-qb-selector="product-item"
6018                    data-product-id="55"
6019                  
6020                    data-pid="55"
6021                    data-s-title="VEGDOG Vuxen Linser &amp; Hirs 400g/800g"
6022                    data-s-price="45.00"
6023                    data-price="45.00"
6024                  >
6025                  
6026                    <!-- Image -->
6027                    <a class="block relative w-full " href="/hundmat/vegdog-adult-400g">
6028                      <div class="relative group overflow-hidden rounded-tnone">
6029                        <img loading="lazy" 
6030                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
6031                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a138de4e49.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Vuxen Linser &amp; Hirs 400g/800g">
6032                        
6033                        <img loading="lazy"
6034                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
6035                          ix-src="https://cdn.quickbutik.com/images/2666L/products/656a138de15dc.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="VEGDOG Vuxen Linser &amp; Hirs 400g/800g">
6036                        
6037                      </div>
6038                    </a>
6039                  
6040                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
6041                      <a class="block px-6 mb-4" href="/hundmat/vegdog-adult-400g">
6042                        
6043                        <!-- Title -->
6044                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
6045                          VEGDOG Vuxen Linser &amp; Hirs 400g/800g
6046                        </h3>
6047                  
6048                        <!-- Reviews container -->
6049                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
6050                  
6051                      </a>
6052                  
6053                      <!-- Description -->
6054                      <template x-if="layoutSingle">
6055                        
6056                      </template>
6057                  
6058                      <div>
6059                  
6060                        <!-- Price -->
6061                        <div class="px-6">
6062                  
6063                          <!-- Current price -->
6064                          <span>
6065                            <span class="text-xl font-bold text-accent">45 SEK</span>
6066                          </span>
6067                          
6068                          <!-- Original price -->
6069                  
6070                        </div>
6071                  
6072                        <!-- Add to basket -->
6073                        <div class="flex justify-between gap-2 px-6 mt-2">
6074                  
6075                            <!-- Buy button -->
6076                              
6077                              <!-- No options -->
6078                  
6079                              <!-- Has options -->
6080                              <a href="/hundmat/vegdog-adult-400g" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
6081                  
6082                                <!-- Icon (arrow) -->
6083                  
6084                                <!-- Text -->
6085                                Läs mer
6086                  
6087                              </a>
6088                  
6089                              <!-- Sold out -->
6090                            
6091                  
6092                  
6093                            
6094                            <!-- Link to product when no buy button -->
6095                  
6096                            <!-- Wishlist -->
6097                            <div data-qb-selector="product-item-wishlist" data-product-id="55" class="flex items-center"></div>
6098                  
6099                        </div>
6100                        
6101                        <!-- Stock status -->
6102                        <div class="text-sm px-6 pt-4">
6103                  
6104                          <!-- Sold out -->
6105                  
6106                          <!-- Not sold out -->
6107                          <span data-in-stock-text="I lager"
6108                            data-out-of-stock-text=""
6109                            data-combination-not-exist-text="Variant finns ej">
6110                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
6111                          </span>
6112                  
6113                        </div>
6114                      
6115                      </div>
6116                    </div>
6117                  </div>
6118      
6119      
6120
6121
6122
6123                  
6124
6125                  
6126
6127
6128
6129                  
6130
6131                  
6132                  <div x-data="{ layoutSingle: '4' == '1' }"
6133                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
6134                  
6135                    data-qb-selector="product-item"
6136                    data-product-id="54"
6137                  
6138                    data-pid="54"
6139                    data-s-title="V-dog Crunchy Nuggets 15kg"
6140                    data-s-price="999"
6141                    data-price="999"
6142                  >
6143                  
6144                    <!-- Image -->
6145                    <a class="block relative w-full " href="/hundmat/v-dog-nuggets-15kg">
6146                      <div class="relative group overflow-hidden rounded-tnone">
6147                        <img loading="lazy" 
6148                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
6149                          ix-src="https://cdn.quickbutik.com/images/2666L/products/62c14897a76c9.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="V-dog Crunchy Nuggets 15kg">
6150                        
6151                        <img loading="lazy"
6152                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
6153                          ix-src="https://cdn.quickbutik.com/images/2666L/products/59f4621c9d505.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="V-dog Crunchy Nuggets 15kg">
6154                        
6155                      </div>
6156                    </a>
6157                  
6158                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
6159                      <a class="block px-6 mb-4" href="/hundmat/v-dog-nuggets-15kg">
6160                        
6161                        <!-- Title -->
6162                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
6163                          V-dog Crunchy Nuggets 15kg
6164                        </h3>
6165                  
6166                        <!-- Reviews container -->
6167                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
6168                  
6169                      </a>
6170                  
6171                      <!-- Description -->
6172                      <template x-if="layoutSingle">
6173                        
6174                      </template>
6175                  
6176                      <div>
6177                  
6178                        <!-- Price -->
6179                        <div class="px-6">
6180                  
6181                          <!-- Current price -->
6182                          <span>
6183                            <span class="text-xl font-bold text-accent">999 SEK</span>
6184                          </span>
6185                          
6186                          <!-- Original price -->
6187                  
6188                        </div>
6189                  
6190                        <!-- Add to basket -->
6191                        <div class="flex justify-between gap-2 px-6 mt-2">
6192                  
6193                            <!-- Buy button -->
6194                              
6195                              <!-- No options -->
6196                              <button @click="$store.cart.add({
6197                                          productID: 54,
6198                                          quantity: 1
6199                                        })"
6200                                aria-label="Lägg i korgen"
6201                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
6202                                
6203                                <!-- Icon (cart) -->
6204                                
6205                                <!-- Text -->
6206                                Lägg i korgen
6207                  
6208                              </button>
6209                  
6210                              <!-- Has options -->
6211                  
6212                              <!-- Sold out -->
6213                            
6214                  
6215                  
6216                            
6217                            <!-- Link to product when no buy button -->
6218                  
6219                            <!-- Wishlist -->
6220                            <div data-qb-selector="product-item-wishlist" data-product-id="54" class="flex items-center"></div>
6221                  
6222                        </div>
6223                        
6224                        <!-- Stock status -->
6225                        <div class="text-sm px-6 pt-4">
6226                  
6227                          <!-- Sold out -->
6228                  
6229                          <!-- Not sold out -->
6230                          <span data-in-stock-text="I lager"
6231                            data-out-of-stock-text=""
6232                            data-combination-not-exist-text="Variant finns ej">
6233                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
6234                          </span>
6235                  
6236                        </div>
6237                      
6238                      </div>
6239                    </div>
6240                  </div>
6241      
6242      
6243
6244
6245
6246                  
6247
6248                  
6249
6250
6251
6252                  
6253
6254                  
6255                  <div x-data="{ layoutSingle: '4' == '1' }"
6256                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
6257                  
6258                    data-qb-selector="product-item"
6259                    data-product-id="15"
6260                  
6261                    data-pid="15"
6262                    data-s-title="Benevo Grain-Free Vegetable Feast 395g"
6263                    data-s-price="59"
6264                    data-price="59"
6265                  >
6266                  
6267                    <!-- Image -->
6268                    <a class="block relative w-full " href="/hundmat/benevo-grain-free-vegetable-feast">
6269                      <div class="relative group overflow-hidden rounded-tnone">
6270                        <img loading="lazy" 
6271                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
6272                          ix-src="https://cdn.quickbutik.com/images/2666L/products/69af30b7576d5.webp" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Benevo Grain-Free Vegetable Feast 395g">
6273                        
6274                        <img loading="lazy"
6275                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
6276                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66a649d473349.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Benevo Grain-Free Vegetable Feast 395g">
6277                        
6278                      </div>
6279                    </a>
6280                  
6281                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
6282                      <a class="block px-6 mb-4" href="/hundmat/benevo-grain-free-vegetable-feast">
6283                        
6284                        <!-- Title -->
6285                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
6286                          Benevo Grain-Free Vegetable Feast 395g
6287                        </h3>
6288                  
6289                        <!-- Reviews container -->
6290                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
6291                  
6292                      </a>
6293                  
6294                      <!-- Description -->
6295                      <template x-if="layoutSingle">
6296                        
6297                      </template>
6298                  
6299                      <div>
6300                  
6301                        <!-- Price -->
6302                        <div class="px-6">
6303                  
6304                          <!-- Current price -->
6305                          <span>
6306                            <span class="text-xl font-bold text-accent">59 SEK</span>
6307                          </span>
6308                          
6309                          <!-- Original price -->
6310                  
6311                        </div>
6312                  
6313                        <!-- Add to basket -->
6314                        <div class="flex justify-between gap-2 px-6 mt-2">
6315                  
6316                            <!-- Buy button -->
6317                              
6318                              <!-- No options -->
6319                              <button @click="$store.cart.add({
6320                                          productID: 15,
6321                                          quantity: 1
6322                                        })"
6323                                aria-label="Lägg i korgen"
6324                                class="qs-cart-submit  px-2 w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted transition duration-default font-bold text-sm button-rounding focus:outline-none">
6325                                
6326                                <!-- Icon (cart) -->
6327                                
6328                                <!-- Text -->
6329                                Lägg i korgen
6330                  
6331                              </button>
6332                  
6333                              <!-- Has options -->
6334                  
6335                              <!-- Sold out -->
6336                            
6337                  
6338                  
6339                            
6340                            <!-- Link to product when no buy button -->
6341                  
6342                            <!-- Wishlist -->
6343                            <div data-qb-selector="product-item-wishlist" data-product-id="15" class="flex items-center"></div>
6344                  
6345                        </div>
6346                        
6347                        <!-- Stock status -->
6348                        <div class="text-sm px-6 pt-4">
6349                  
6350                          <!-- Sold out -->
6351                  
6352                          <!-- Not sold out -->
6353                          <span data-in-stock-text="I lager"
6354                            data-out-of-stock-text=""
6355                            data-combination-not-exist-text="Variant finns ej">
6356                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
6357                          </span>
6358                  
6359                        </div>
6360                      
6361                      </div>
6362                    </div>
6363                  </div>
6364      
6365      
6366
6367
6368
6369                  
6370
6371                  
6372
6373
6374
6375                  
6376
6377                  
6378                  <div x-data="{ layoutSingle: '4' == '1' }"
6379                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
6380                  
6381                    data-qb-selector="product-item"
6382                    data-product-id="13"
6383                  
6384                    data-pid="13"
6385                    data-s-title="Benevo Duo 354g"
6386                    data-s-price="45.00"
6387                    data-price="45.00"
6388                  >
6389                  
6390                    <!-- Image -->
6391                    <a class="block relative w-full " href="/hundmat/benevo-duo">
6392                      <div class="relative group overflow-hidden rounded-tnone">
6393                        <img loading="lazy" 
6394                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
6395                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65f94db2d5ba3.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Benevo Duo 354g">
6396                        
6397                        <img loading="lazy"
6398                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
6399                          ix-src="https://cdn.quickbutik.com/images/2666L/products/65f94db4a03a0.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Benevo Duo 354g">
6400                        
6401                      </div>
6402                    </a>
6403                  
6404                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
6405                      <a class="block px-6 mb-4" href="/hundmat/benevo-duo">
6406                        
6407                        <!-- Title -->
6408                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
6409                          Benevo Duo 354g
6410                        </h3>
6411                  
6412                        <!-- Reviews container -->
6413                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
6414                  
6415                      </a>
6416                  
6417                      <!-- Description -->
6418                      <template x-if="layoutSingle">
6419                        
6420                      </template>
6421                  
6422                      <div>
6423                  
6424                        <!-- Price -->
6425                        <div class="px-6">
6426                  
6427                          <!-- Current price -->
6428                          <span>
6429                            <span class="text-xl font-bold text-accent">45 SEK</span>
6430                          </span>
6431                          
6432                          <!-- Original price -->
6433                  
6434                        </div>
6435                  
6436                        <!-- Add to basket -->
6437                        <div class="flex justify-between gap-2 px-6 mt-2">
6438                  
6439                            <!-- Buy button -->
6440                              
6441                              <!-- No options -->
6442                  
6443                              <!-- Has options -->
6444                              <a href="/hundmat/benevo-duo" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
6445                  
6446                                <!-- Icon (arrow) -->
6447                  
6448                                <!-- Text -->
6449                                Läs mer
6450                  
6451                              </a>
6452                  
6453                              <!-- Sold out -->
6454                            
6455                  
6456                  
6457                            
6458                            <!-- Link to product when no buy button -->
6459                  
6460                            <!-- Wishlist -->
6461                            <div data-qb-selector="product-item-wishlist" data-product-id="13" class="flex items-center"></div>
6462                  
6463                        </div>
6464                        
6465                        <!-- Stock status -->
6466                        <div class="text-sm px-6 pt-4">
6467                  
6468                          <!-- Sold out -->
6469                  
6470                          <!-- Not sold out -->
6471                          <span data-in-stock-text="I lager"
6472                            data-out-of-stock-text=""
6473                            data-combination-not-exist-text="Variant finns ej">
6474                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
6475                          </span>
6476                  
6477                        </div>
6478                      
6479                      </div>
6480                    </div>
6481                  </div>
6482      
6483      
6484
6485
6486
6487                  
6488
6489                  
6490
6491
6492
6493                  
6494
6495                  
6496                  <div x-data="{ layoutSingle: '4' == '1' }"
6497                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
6498                  
6499                    data-qb-selector="product-item"
6500                    data-product-id="12"
6501                  
6502                    data-pid="12"
6503                    data-s-title="Benevo Dog Original"
6504                    data-s-price="199.00"
6505                    data-price="199.00"
6506                  >
6507                  
6508                    <!-- Image -->
6509                    <a class="block relative w-full " href="/hundmat/benevo-dog-original">
6510                      <div class="relative group overflow-hidden rounded-tnone">
6511                        <img loading="lazy" 
6512                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
6513                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66018090c6104.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Benevo Dog Original">
6514                        
6515                        <img loading="lazy"
6516                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
6517                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66018092cc5a4.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="Benevo Dog Original">
6518                        
6519                      </div>
6520                    </a>
6521                  
6522                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
6523                      <a class="block px-6 mb-4" href="/hundmat/benevo-dog-original">
6524                        
6525                        <!-- Title -->
6526                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
6527                          Benevo Dog Original
6528                        </h3>
6529                  
6530                        <!-- Reviews container -->
6531                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
6532                  
6533                      </a>
6534                  
6535                      <!-- Description -->
6536                      <template x-if="layoutSingle">
6537                        
6538                      </template>
6539                  
6540                      <div>
6541                  
6542                        <!-- Price -->
6543                        <div class="px-6">
6544                  
6545                          <!-- Current price -->
6546                          <span>
6547                            <span class="text-xl font-bold text-accent">199 SEK</span>
6548                          </span>
6549                          
6550                          <!-- Original price -->
6551                  
6552                        </div>
6553                  
6554                        <!-- Add to basket -->
6555                        <div class="flex justify-between gap-2 px-6 mt-2">
6556                  
6557                            <!-- Buy button -->
6558                              
6559                              <!-- No options -->
6560                  
6561                              <!-- Has options -->
6562                              <a href="/hundmat/benevo-dog-original" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
6563                  
6564                                <!-- Icon (arrow) -->
6565                  
6566                                <!-- Text -->
6567                                Läs mer
6568                  
6569                              </a>
6570                  
6571                              <!-- Sold out -->
6572                            
6573                  
6574                  
6575                            
6576                            <!-- Link to product when no buy button -->
6577                  
6578                            <!-- Wishlist -->
6579                            <div data-qb-selector="product-item-wishlist" data-product-id="12" class="flex items-center"></div>
6580                  
6581                        </div>
6582                        
6583                        <!-- Stock status -->
6584                        <div class="text-sm px-6 pt-4">
6585                  
6586                          <!-- Sold out -->
6587                  
6588                          <!-- Not sold out -->
6589                          <span data-in-stock-text="I lager"
6590                            data-out-of-stock-text=""
6591                            data-combination-not-exist-text="Variant finns ej">
6592                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
6593                          </span>
6594                  
6595                        </div>
6596                      
6597                      </div>
6598                    </div>
6599                  </div>
6600      
6601      
6602
6603
6604
6605                  
6606
6607                  
6608
6609
6610
6611                  
6612
6613                  
6614                  <div x-data="{ layoutSingle: '4' == '1' }"
6615                    class="product flex items-center h-full flex-1 relative bg-card shadow roundednone flex-col overflow-hidden"
6616                  
6617                    data-qb-selector="product-item"
6618                    data-product-id="5"
6619                  
6620                    data-pid="5"
6621                    data-s-title="AMI DOG Medium &amp; Large 3kg/12kg"
6622                    data-s-price="349.00"
6623                    data-price="349.00"
6624                  >
6625                  
6626                    <!-- Image -->
6627                    <a class="block relative w-full " href="/hundmat/ami-dog">
6628                      <div class="relative group overflow-hidden rounded-tnone">
6629                        <img loading="lazy" 
6630                          class="w-full object-contain object-center aspect-1/1 group-hover:opacity-0 transition duration-default"
6631                          ix-src="https://cdn.quickbutik.com/images/2666L/products/66b0638482069.png" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Medium &amp; Large 3kg/12kg">
6632                        
6633                        <img loading="lazy"
6634                          class="w-full absolute top-0 left-0 object-contain object-center aspect-1/1 opacity-0 group-hover:opacity-100 transition duration-default"
6635                          ix-src="https://cdn.quickbutik.com/images/2666L/products/67d938b2248ce.jpeg" ix-params='{"auto":"format,compress"}' ix-sizes="auto" alt="AMI DOG Medium &amp; Large 3kg/12kg">
6636                        
6637                      </div>
6638                    </a>
6639                  
6640                    <div class="flex flex-1 flex-col justify-between md:h-full w-full py-6" :class="layoutSingle && 'md:w-3/4'">
6641                      <a class="block px-6 mb-4" href="/hundmat/ami-dog">
6642                        
6643                        <!-- Title -->
6644                        <h3 class="text-base sm:text-xl font-heading leading-6 font-bold">
6645                          AMI DOG Medium &amp; Large 3kg/12kg
6646                        </h3>
6647                  
6648                        <!-- Reviews container -->
6649                        <div class="d-flex mt-2" data-qb-selector="product-apps-reviews-item-reviews"></div>
6650                  
6651                      </a>
6652                  
6653                      <!-- Description -->
6654                      <template x-if="layoutSingle">
6655                        
6656                      </template>
6657                  
6658                      <div>
6659                  
6660                        <!-- Price -->
6661                        <div class="px-6">
6662                  
6663                          <!-- Current price -->
6664                          <span>
6665                            <span class="text-xl font-bold text-accent">349 SEK</span>
6666                          </span>
6667                          
6668                          <!-- Original price -->
6669                  
6670                        </div>
6671                  
6672                        <!-- Add to basket -->
6673                        <div class="flex justify-between gap-2 px-6 mt-2">
6674                  
6675                            <!-- Buy button -->
6676                              
6677                              <!-- No options -->
6678                  
6679                              <!-- Has options -->
6680                              <a href="/hundmat/ami-dog" area-label="Läs mer" class="block px-2  w-full button-py bg-foreground border border-foreground hover:opacity-80 text-inverted text-center transition duration-default font-bold text-sm button-rounding focus:outline-none">
6681                  
6682                                <!-- Icon (arrow) -->
6683                  
6684                                <!-- Text -->
6685                                Läs mer
6686                  
6687                              </a>
6688                  
6689                              <!-- Sold out -->
6690                            
6691                  
6692                  
6693                            
6694                            <!-- Link to product when no buy button -->
6695                  
6696                            <!-- Wishlist -->
6697                            <div data-qb-selector="product-item-wishlist" data-product-id="5" class="flex items-center"></div>
6698                  
6699                        </div>
6700                        
6701                        <!-- Stock status -->
6702                        <div class="text-sm px-6 pt-4">
6703                  
6704                          <!-- Sold out -->
6705                  
6706                          <!-- Not sold out -->
6707                          <span data-in-stock-text="I lager"
6708                            data-out-of-stock-text=""
6709                            data-combination-not-exist-text="Variant finns ej">
6710                            <span class="inline-block bg-green-600 rounded-full h-2 w-2 mr-1"></span> I lager
6711                          </span>
6712                  
6713                        </div>
6714                      
6715                      </div>
6716                    </div>
6717                  </div>
6718      
6719                
6720              </div>
6721            </div>
6722          </div>
6723      
6724          <!-- Pagination -->
6725      
6726          <!-- Description (2) -->
6727      
6728        </div>
6729      </section>
6730      <section data-qb-area="footer" class="bg-footer text-center lg:text-left">
6731        <div class="container px-4 py-8 md:py-12 mx-auto ">
6732          <div class="flex flex-wrap justify-between">
6733            <div class="w-full lg:w-1/3 mb-10 lg:mb-0 px-4">
6734              
6735              <!-- Logotype -->
6736              <a data-qb-setting="use_footer_logo" class="inline-block mb-6 w-full h-8" href="/">
6737                <img src="https://cdn.quickbutik.com/images/2666L/templates/lua/assets/logo.png?s=96971&auto=format&fit=clip&h=160&w=600" alt="Vecanis" class="h-8 w-full max-w-xs mx-auto lg:mx-0 object-contain object-center lg:object-left" >
6738              </a>
6739      
6740              <!-- Text below logotype -->
6741      
6742              <!-- Social media link -->
6743              <div data-qb-setting="show_footer_socialicons" class="text-footer opacity-70 mb-3">
6744      
6745                <a class="inline-block mr-4 hover:opacity-70" aria-label="Facebook" href="https://www.facebook.com/vecanis.se/" target="_blank">
6746                  <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24">
6747                    <path fill="currentColor" d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"></path>
6748                  </svg>
6749                </a>
6750                <a
6751                  class="inline-block mr-4 hover:opacity-70"
6752                  aria-label="Instagram"
6753                  href="https://instagram.com/vecanis/" target="_blank">
6754                  <svg
6755                    xmlns="http://www.w3.org/2000/svg"
6756                    class="w-4 h-4"
6757                    viewBox="0 0 24 24"
6758                  >
6759                    <path
6760                      fill="currentColor"
6761                      d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"
6762                    ></path>
6763                  </svg>
6764                </a>
6765              </div>
6766            </div>
6767      
6768            <div class="w-full lg:w-1/3 lg:pl-24 px-4 mb-10 lg:mb-0">
6769              
6770              <!-- Footer menu -->
6771              <h5 data-qb-setting="footer_menu_heading" class="text-lg font-bold mb-5 text-footer">
6772                Läs mer
6773              </h5>
6774      
6775              <ul data-qb-setting="use_footer_menu" class="opacity-70">
6776                <li class="mb-2">
6777                  <a class="inline-block text-footer hover:opacity-70" href="/contact">Kontakt</a>
6778                </li>
6779                <li class="mb-2">
6780                  <a class="inline-block text-footer hover:opacity-70" href="/sidor/terms-and-conditions">Köpvillkor</a>
6781                </li>
6782                <li class="mb-2">
6783                  <a class="inline-block text-footer hover:opacity-70" href="/sidor/faq">Vanliga frågor</a>
6784                </li>
6785                <li class="mb-2">
6786                  <a class="inline-block text-footer hover:opacity-70" href="/sidor/cookies">Cookies</a>
6787                </li>
6788                <li class="mb-2">
6789                  <a class="inline-block text-footer hover:opacity-70" href="/sidor/gdpr">GDPR</a>
6790                </li>
6791              </ul>
6792            </div>
6793      
6794            <!-- Newsletter signup -->
6795            <div class="w-full lg:w-1/3 px-4">
6796              <h5 data-qb-setting="footer_newsletter_header" class="text-lg font-bold mb-5 text-footer">Sugen på rabatter och uppdateringar? Prenumerera på vårt nyhetsbrev</h5>
6797      
6798              <form id="footer_newsletter_form" action="/shop/newsletter"
6799                data-txt-failed='Ange din e-postadress'
6800                data-txt-error='Ett fel uppstod, försök igen senare'
6801                data-txt-success='Du har nu anmält dig till vårt nyhetsbrev!'>
6802      
6803                <div class="flex items-center pl-3 pr-2 py-2 bg-white button-rounding">
6804                  <input
6805                    data-qb-setting="footer_newsletter_placeholder_text"
6806                    class="w-full px-2 inline-block bg-transparent border-none text-base outline-none"
6807                    type="email" name="email" placeholder="Ange e-postadress"
6808                    aria-label="Ange e-postadress">
6809                  <button data-qb-setting="footer_newsletter_button_text" type="submit" class="ml-auto inline-block px-3 py-2 bg-black hover:opacity-90 text-white font-bold transition duration-default button-rounding" href="#">Prenumerera</button>
6810                </div>
6811              </form>
6812              <p id="footer_newsletter_status" class="text-sm text-footer" role="status" aria-live="polite"></p>
6813      
6814              
6814<script>
6815                function showNewsletterStatus(text) {
6816                  const line = document.createElement('span')
6817                  line.className = 'block mt-4'
6818                  line.textContent = text
6819                  newsletterStatus.replaceChildren(line)
6820                }
6821      
6822                function logSubmit(event) {
6823                  event.preventDefault()
6824                  newsletterStatus.replaceChildren()
6825      
6826                  fetch(event.target.action, {
6827                    method: 'POST',
6828                    body: new FormData(event.target),
6829                  })
6830                  .then(function(response) {
6831                    if (response.ok) {
6832                      return response.json()
6833                    }
6834                    return Promise.reject(response)
6835                  })
6836                  .then(function(data) {
6837                    if (data.success) {
6838                      form.querySelector('input[name="email"]').value = ''
6839      
6840                      showNewsletterStatus(event.target.dataset.txtSuccess);
6841                    } else if (data.error) {
6842                      showNewsletterStatus(event.target.dataset.txtFailed);
6843                    } else {
6844                      showNewsletterStatus(event.target.dataset.txtError);
6845                    }
6846                  }).catch(function (error) {
6847                    showNewsletterStatus(event.target.dataset.txtError);
6848                  });
6849                }
6850      
6851                const form = document.getElementById("footer_newsletter_form");
6852                const newsletterStatus = document.getElementById("footer_newsletter_status");
6853                form.addEventListener("submit", logSubmit);
6854              </script>
6854
6855      
6856            </div>
6857      
6858          </div>
6859      
6860          <div>
6861            <!-- Payment methods -->
6862            <div data-qb-setting="use_footer_paymentmethods" class="flex justify-center items-center gap-default flex-wrap mt-10">
6863      
6864      
6865      
6866              <img loading="lazy" class="h-10 p-2 w-auto" src="/assets/shopassets/paylogos/payment-visa-blue.svg" alt="Payment with Visa">
6867      
6868      
6869              <img loading="lazy" class="h-10 p-2 w-auto" src="/assets/shopassets/paylogos/payment-mastercard.svg" alt="Payment with Mastercard">
6870      
6871              <img loading="lazy" class="h-10 p-2 w-auto" src="/assets/shopassets/paylogos/payment-klarna.svg" alt="Payment with Klarna">
6872      
6873      
6874      
6875      
6876      
6877      
6878              <img loading="lazy" class="h-10 p-2 w-auto" src="/assets/shopassets/paylogos/payment-paypal-color.svg" alt="Payment with Paypal">
6879      
6880      
6881      
6882              <img loading="lazy" class="h-10 p-2 w-auto" src="/assets/shopassets/paylogos/payment-swish.png" alt="Payment with Swish">
6883      
6884            </div>
6885          </div>
6886        </div>
6887      
6888        <div>
6889          <hr class="border-foreground opacity-40">
6890          <div class="container px-4 mx-auto">
6891            <div class="py-8 text-center">
6892              <p class="text-footer text-sm opacity-50">
6893                <!-- Copyright -->
6894                © 2026 Vecanis
6895                <span data-qb-setting="use_footer_poweredby">&ndash; <a style="text-decoration: none;" target="_blank" rel="nofollow sponsored" title="Powered by Quickbutik" href="https://quickbutik.com">Powered by Quickbutik</a></span>
6896              </p>
6897            </div>
6898          </div>
6899        </div>
6900      </section>
6901      <div x-data x-cloak
6902        x-show="$store.cart.cartOpen"
6903        @keydown.escape="$store.cart.hideCart()"
6904        @pageshow.window="$store.cart.init()"
6905        class="navbar-menu fixed top-0 right-0 bottom-0 w-full sm:max-w-sm z-50">
6906      
6907        <!-- Overlay -->
6908        <div
6909          x-show='$store.cart.cartOpen'
6910          @click='$store.cart.hideCart()'
6911          x-transition:enter='transition ease-out duration-default transform'
6912          x-transition:enter-start='opacity-0'
6913          x-transition:enter-end='opacity-100'
6914          x-transition:leave='transition ease-in duration-default transform'
6915          x-transition:leave-start='opacity-100'
6916          x-transition:leave-end='opacity-0'
6917          class='fixed inset-0 transition-opacity bg-overlay bg-opacity-40'
6918          aria-hidden='true'
6919        ></div>
6920        
6921        <!-- Cart -->
6922        <div
6923          x-show='$store.cart.cartOpen'
6924          @keydown.escape="$store.cart.hideCart()"
6925          class="relative flex flex-col h-full bg-body"
6926          x-transition:enter='transition ease-out duration-default transform'
6927          x-transition:enter-start='opacity-0 translate-x-10 sm:translate-x-10'
6928          x-transition:enter-end='opacity-100 translate-x-0'
6929          x-transition:leave='transition ease-in duration-default transform'
6930          x-transition:leave-start='opacity-100 translate-x-0'
6931          x-transition:leave-end='opacity-0 translate-x-10 sm:translate-x-10'
6932        >
6933          <header class="flex items-center justify-between p-6 mb-4">
6934            
6935            <!-- Title -->
6936            <span class="items-center font-heading text-3xl">
6937              Varukorg
6938            </span>
6939      
6940            <div class="flex">
6941      
6942              <!-- Close button -->
6943              <button
6944                @click="$store.cart.hideCart()"
6945                class="inline-block cart-close cursor-pointer focus:outline-none hover:opacity-50"
6946                type="button"
6947                aria-label="Close"
6948              >
6949                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
6950                  <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path>
6951                </svg>
6952              </button>
6953      
6954            </div>
6955      
6956          </header>
6957      
6958          <!-- Cart contents -->
6959          <div class="h-full p-6 overflow-y-auto">
6960      
6961            <!-- Cart messages -->
6962            <div class="qs-cart-message"></div>
6963      
6964            <!-- Empty cart snippet-->
6965            <!-- In case of empty cart - display this snippet + remove .d-none-->
6966            <div class="hidden qs-cart-empty flex items-center h-full  opacity-50 text-center">
6967              <div>
6968                <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="inline-block w-16 h-16 mb-6">
6969                  <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 10.5V6a3.75 3.75 0 10-7.5 0v4.5m11.356-1.993l1.263 12c.07.665-.45 1.243-1.119 1.243H4.25a1.125 1.125 0 01-1.12-1.243l1.264-12A1.125 1.125 0 015.513 7.5
6969h12.974c.576 0 1.059.435 1.119 1.007zM8.625 10.5a.375.375 0 11-.75 0 .375.375 0 01.75 0zm7.5 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"></path>
6970                </svg>
6971                <p>
6972                  Varukorgen är tom
6973                </p>
6974              </div>
6975            </div>
6976      
6977            <!-- Cart items -->
6978            <template
6979              x-for="product in $store.cart.products"
6980              :key="`${product.item.id}-${product.qty}-${product.uid}`"
6981            >
6982              <div class="mb-6">
6983                
6984                <div class="flex w-full">
6985      
6986                  <!-- Product image -->
6987                  <div class="flex justify-center items-center pr-4 w-24">
6988                    <a :href="product.item.url">
6989                      <img loading="lazy" :src="product.item.firstimage + '?w=100&h=100'" class="object-contain object-center w-full h-full roundednone" :alt="product.item.title">
6990                    </a>
6991                  </div>
6992                  <div class="flex flex-1 flex-col">
6993                    
6994                    <!-- Product title -->
6995                    <a :href="product.item.url">
6996                      <h4 class="font-body font-bold" x-text="product.item.title"></h4>
6997                    </a>
6998                    
6999                    <!-- Product variant -->
7000                    <template x-if="product.variant_name">
7001                      <small x-text="product.variant_name" class="opacity-50"></small>
7002                    </template>
7003      
7004                    <!-- Product propertiers -->
7005                    <template x-if="product.properties">
7006                      <template x-for="property in Object.values(product.properties)">
7007                        <small class="break-words" x-text="property"></small>
7008                      </template>
7009                    </template>
7010      
7011                    <!-- Price -->
7012                      <!-- Current price -->
7013                      <span class="text-sm">
7014                        <span
7015                          :class="product.item.has_discount ? 'text-discounted-price' : ''"
7016                          x-text="product.item.price"
7017                        ></span>
7018      
7019                        <!-- Original price (only show if has discount) -->
7020                        <template x-if="product.item.has_discount">
7021                          <span
7022                            class="opacity-50 line-through ml-1"
7023                            x-text="product.item.original_price"
7024                          ></span>
7025                        </template>            
7026                      </span>
7027      
7028                    <div
7029                      class="flex-1 flex items-center justify-between mt-2"
7030                      x-data="{
7031                        quantity: product.qty,
7032                      }"
7033                      x-init="$watch('quantity',  Alpine.debounce((value) => {
7034                        if (value && value > 0) {
7035                          $store.cart.update({
7036                            productID: product.item.id, 
7037                            quantity: value
7038                          })
7039                        }
7040                      }, 500))"
7041                    >
7042                      <div class="flex border border-gray-200 input-rounding overflow-hidden">
7043      
7044                        <!-- Decrease quantity -->
7045                        <button
7046                          type="button"
7047                          class="w-8 font-bold cursor-pointer input-bg focus:outline-none hover:bg-gray-100 py-1 px-2"
7048                          @click="quantity = Math.max(1, quantity - 1)"
7049                        >
7050                          -
7051                        </button>
7052      
7053                        <!-- Quantity input -->
7054                        <input
7055                          required
7056                          min="1"
7057                          step="1"
7058                          type="number"
7059                          class="w-12 qs-cart-qty navbar-cart-product-qty input-bg border-0 input-items text-center focus:outline-none"
7060                          x-model="quantity"
7061                          @input="quantity = Math.floor(Number(quantity))"
7062                        >
7063      
7064                        <!-- Increase quantity -->
7065                        <button
7066                          type="button"
7067                          class="w-8 font-bold cursor-pointer input-bg focus:outline-none hover:bg-gray-100 py-1 px-2"
7068                          @click="quantity = Number(quantity) + 1"
7069                        >
7070                          +
7071                        </button>
7072                      </div>
7073      
7074                      <!-- Remove from cart -->
7075                      <button
7076                        type="button"
7077                        class="font-medium text-baseThird qs-cart-delete btn hover:text-red-700"
7078                        data-pid=""
7079                        @click="$store.cart.delete({
7080                          productID: product.item.id,
7081                        })"
7082                      >
7083                        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
7084                          <path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
7085                        </svg>
7086                      </button>
7087                    </div>
7088      
7089                  </div>
7090                </div>
7091      
7092              </div>
7093            </template>
7094          </div>
7095      
7096          <!-- Footer -->
7097      
7098          <!-- Tax -->
7099      
7100          <footer class="mt-auto p-8 bg-gray-400 bg-opacity-5">
7101      
7102            <!-- Total discount (only show if there's a discount) -->
7103            <div class="flex items-end mb-2" x-show="$store.cart.totalDiscountRaw && $store.cart.totalDiscountRaw > 0">
7104              <div class="flex-1">
7105                <span class="text-sm font-medium text-discounted-price">Rabatt</span>
7106              </div>
7107              <div class="flex-1 text-right">
7108                <span
7109                  class="text-sm font-medium text-discounted-price"
7110                  x-text="'-' + $store.cart.totalDiscount"
7111                ></span>
7112              </div>
7113            </div>
7114      
7115            <div class="flex mb-4">
7116              <div class="flex-1">
7117                <span class="text-lg font-bold">Total summa:</span>
7118              </div>
7119              <div class="flex-1 text-right">
7120                <span
7121                  class="text-lg font-bold text-primary"
7122                  x-text="$store.cart.totalAmount"
7123                ></span>
7124              </div>
7125            </div>
7126            <div>
7127              <button @click='$store.cart.hideCart()' class="block w-full button-px button-py mb-2 text-center text-primary font-bold hover:text-body button-rounding focus:outline-none">
7128                Fortsätt att handla
7129              </button>
7130              <a href="/cart/index" class="block button-px button-py text-center text-white font-bold bg-primary hover:bg-gray-900 button-rounding focus:outline-none">
7131                Till kassan
7132              </a>
7133            </div>
7134          </footer>
7135      
7136        </div>
7137      </div>
7138      <section 
7139        x-data
7140        x-cloak
7141        x-show="$store.modal.modalVisible"
7142        x-transition.opacity
7143        class="fixed inset-0 py-24 bg-black bg-opacity-50 z-50 h-full overflow-y-scroll">
7144          <div class="container px-4 mx-auto relative">
7145          <div x-on:click.outside="$store.modal.closeModal()" :style="{'background-color': $store.modal.backgroundColor || '#fff'}"
7146          class="relative max-w-xl mx-auto text-center py-12 md:py-16 px-6 md:px-14 roundednone">
7147            <a class="absolute top-4 right-4 inline-block ml-auto cursor-pointer hover:opacity-50" x-on:click="$store.modal.closeModal()" href="#" rel="nofollow">
7148              <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
7149                <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path>
7150              </svg>
7151            </a>
7152            <div x-html="$store.modal.content"></div>
7153          </div>
7154        </div>
7155      </section>
7156<script type="text/javascript"> var recaptchaSiteKey="6Lc-2GcUAAAAAMXG8Lz5S_MpVshTUeESW6iJqK0_"; </script>
7156<script type="text/javascript">window.registerApp({
7157  name: 'Store Notifications',
7158  identifier: 'QuickbutikStoreNotifications',
7159  description: 'Show store notifications',
7160  version: '1.0.1',
7161  pages: ['list', 'start', 'product'],
7162  data: {},
7163  config: {},
7164  onMount: function ({ appData, page }) {
7165    console.debug(`${this.identifier}@onMount`)
7166
7167    const content = appData?.storenotices?.text
7168    const nid = appData?.storenotices?.nid
7169
7170    if (content) {
7171      Alpine.store('notifications').addNotification({
7172        type: 'html',
7173        htmlContent: content,
7174        lifeTime: appData.storenotices.timer_seconds === "0" || appData.storenotices.timer_seconds === 0
7175          ? null
7176          : appData.storenotices.timer_seconds
7177          ? appData.storenotices.timer_seconds * 1000
7178          : 5000,
7179        backgroundColor: appData.storenotices.bg_color,
7180        textColor: appData.storenotices.text_color,
7181        onRemove: () => {
7182          if (nid) {
7183            const formData = new FormData()
7184            formData.append('nid', nid)
7185
7186            fetch('/apps/storenotices/shown', {
7187              method: 'POST',
7188              body: formData,
7189              keepalive: true,
7190            })
7191          }
7192        },
7193      })
7194    }
7195  },
7196})
7197</script>
7197<script type="text/javascript">window.registerApp({
7198  name: 'Popups',
7199  identifier: 'QuickbutikPopups',
7200  description: 'Display popups when page loads',
7201  version: '2.0.0',
7202  pages: ['list', 'start', 'product'],
7203  data: {},
7204  config: {
7205    style: {
7206      popup: {
7207        containerClasses: 'qb-popup-container',
7208        titleClasses: 'qb-popup-title',
7209        textClasses: 'qb-popup-text',
7210        buttonClasses: 'qb-popup-button',
7211        inputClasses: 'qb-popup-input',
7212        imageClasses: 'qb-popup-image',
7213      },
7214    },
7215    renderPopup: null,
7216    renderSecondPage: null,
7217  },
7218  _cssInjected: false,
7219  _minimizeTab: null,
7220  _goalCompleted: false,
7221  onMount: function ({ appData, page }) {
7222    if (new URLSearchParams(window.location.search).has('_langtags_scan')) return
7223
7224    this._injectStyles()
7225    this._attachSubmitHandler()
7226
7227    appData.storepopups = appData.storepopups
7228      .sort((a, b) => a.on_exit - b.on_exit)
7229      .filter((p) => !sessionStorage.getItem('qb_popup_shown_' + p.ppid))
7230
7231    if (appData?.storepopups?.length) {
7232      this.data.popup = appData.storepopups[0]
7233      const { on_exit } = this?.data?.popup ?? {}
7234
7235      var ppid = this.data.popup.ppid
7236      var popupData = this._buildPopupData(this.data.popup)
7237
7238      if (popupData.minimizeOnClose && sessionStorage.getItem('qb_popup_minimized_' + ppid)) {
7239        this._showMinimizeTab(popupData, ppid)
7240        return
7241      }
7242
7243      const openModalIfNeeded = () => {
7244        if (on_exit === true && !this.mouseLeftDocument) {
7245          return
7246        }
7247
7248        if (this.data.popup) {
7249          this._openPopup(false, popupData)
7250        }
7251      }
7252
7253      if (on_exit === true) {
7254        this.attachMouseLeaveListener(openModalIfNeeded)
7255      } else {
7256        openModalIfNeeded()
7257      }
7258    }
7259  },
7260  _openPopup: function (skipDelay, cachedPopupData) {
7261    var popup = this.data.popup
7262    if (!popup) return
7263
7264    var delay_seconds = popup.delay_seconds
7265    var p_bg_color = popup.p_bg_color
7266    var ppid = popup.ppid
7267
7268    var popupData = cachedPopupData || this._buildPopupData(popup)
7269
7270    Alpine.store('modal').openPopup(
7271      popupData,
7272      (data, state) => this._render(data, state),
7273      {
7274        delay: skipDelay ? 0 : parseInt(delay_seconds ?? 0) * 1000,
7275        backgroundColor: p_bg_color,
7276        position: popupData.position,
7277        modalSize: popupData.modalSize,
7278        imagePosition: popupData.imagePosition,
7279        appearEffect: popupData.appearEffect,
7280        image: popupData.image,
7281        onClose: () => {
7282          if (popupData.minimizeOnClose && !this._goalCompleted) {
7283            this._showMinimizeTab(popupData, ppid)
7284          } else {
7285            this._goalCompleted = false
7286            sessionStorage.removeItem('qb_popup_minimized_' + ppid)
7287            this._markShown(ppid)
7288            this.data.popup = null
7289          }
7290        },
7291      }
7292    )
7293  },
7294  _markShown: function (ppid) {
7295    var formData = new FormData()
7296    formData.append('ppid', ppid)
7297    fetch('/apps/storepopups/shown', { method: 'POST', body: formData })
7298  },
7299  _showMinimizeTab: function (popupData, ppid) {
7300    this._hideMinimizeTab()
7301    sessionStorage.setItem('qb_popup_minimized_' + ppid, '1')
7302
7303    var tab = document.createElement('button')
7304    tab.className = 'qb-popup-minimize-tab'
7305    tab.setAttribute('data-qb-minimize-tab', '')
7306    tab.style.backgroundColor = popupData.bgColor || '#fff'
7307    tab.style.color = popupData.textColor || '#000'
7308
7309    var chevron = document.createElement('span')
7310    chevron.className = 'qb-popup-minimize-tab__chevron'
7311    chevron.innerHTML = '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="2 9 7 4 12 9"/></svg>'
7312
7313    var textSpan = document.createElement('span')
7314    textSpan.textContent = popupData.minimizeText || popupData.title || ''
7315
7316    var closeBtn = document.createElement('span')
7317    closeBtn.className = 'qb-popup-minimize-tab__close'
7318    closeBtn.setAttribute('role', 'button')
7319    closeBtn.setAttribute('aria-label', 'Close')
7320    closeBtn.innerHTML = '<svg width="12" height="12" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="2" y1="2" x2="12" y2="12"/><line x1="12" y1="2" x2="2" y2="12"/></svg>'
7321    closeBtn.addEventListener('click', function (e) {
7322      e.stopPropagation()
7323      this._dismissPopup(ppid)
7324    }.bind(this))
7325
7326    tab.appendChild(chevron)
7327    tab.appendChild(textSpan)
7328    tab.appendChild(closeBtn)
7329
7330    tab.addEventListener('click', function () {
7331      this._reopenPopup()
7332    }.bind(this))
7333
7334    document.body.appendChild(tab)
7335    this._minimizeTab = tab
7336  },
7337  _hideMinimizeTab: function () {
7338    if (this._minimizeTab) {
7339      this._minimizeTab.remove()
7340      this._minimizeTab = null
7341    }
7342  },
7343  _reopenPopup: function () {
7344    this._hideMinimizeTab()
7345    this._openPopup(true)
7346  },
7347  _dismissPopup: function (ppid) {
7348    var tab = this._minimizeTab
7349    this._minimizeTab = null
7350    if (tab) {
7351      tab.classList.add('qb-popup-minimize-tab--exit')
7352      setTimeout(function () { tab.remove() }, 200)
7353    }
7354
7355    sessionStorage.removeItem('qb_popup_minimized_' + ppid)
7356    this._goalCompleted = false
7357    this._markShown(ppid)
7358    this.data.popup = null
7359  },
7360  attachMouseLeaveListener: function (callback) {
7361    document.addEventListener('mouseleave', () => {
7362      this.mouseLeftDocument = true
7363      callback()
7364    })
7365  },
7366  _buildPopupData: function (popup) {
7367    return {
7368      title: popup.p_title,
7369      text: popup.p_text,
7370      textColor: popup.p_text_color,
7371      bgColor: popup.p_bg_color,
7372      btnText: popup.p_btn_text,
7373      btnBgColor: popup.p_btn_bg_color,
7374      btnTextColor: popup.p_btn_text_color,
7375      hasGoal: popup.has_goal,
7376      goalType: popup.goal_type,
7377      textGoal: popup.p_text_goal,
7378      image: popup.p_image || null,
7379      position: popup.position || 'center',
7380      modalSize: popup.modal_size || 'medium',
7381      imagePosition: popup.image_position || 'top',
7382      imageWidth: popup.image_width || 50,
7383      textAlign: popup.text_align || 'center',
7384      showLogo: popup.show_logo == 1,
7385      logoUrl: popup.p_logo_url || null,
7386      appearEffect: popup.appear_effect || 'fade-up',
7387      showImageOnSuccess: popup.show_image_on_success != 0 && popup.show_image_on_success !== false,
7388      minimizeOnClose: popup.minimize_on_close == true || popup.minimize_on_close == 1,
7389      minimizeText: popup.minimize_text || null,
7390      finePrint: popup.fine_print || null,
7391    }
7392  },
7393  _render: function (popupData, state) {
7394    if (state === 'success') {
7395      if (this.config.renderSecondPage) {
7396        return this.config.renderSecondPage(popupData)
7397      }
7398      return this._renderSuccess(popupData)
7399    }
7400
7401    if (this.config.renderPopup) {
7402      return this.config.renderPopup(popupData)
7403    }
7404    return this._renderPopup(popupData)
7405  },
7406  _renderPopup: function (popupData) {
7407    const {
7408      title,
7409      text,
7410      textColor,
7411      btnText,
7412      btnBgColor,
7413      btnTextColor,
7414      hasGoal,
7415      goalType,
7416      image,
7417      imagePosition,
7418      imageWidth,
7419    } = popupData
7420
7421    const cls = this.config.style.popup
7422    const isHorizontal = image && (imagePosition === 'left' || imagePosition === 'right')
7423    const isTopImage = image && imagePosition === 'top'
7424    const isTakeover = image && imagePosition === 'background'
7425    const isCover = image && imagePosition === 'cover'
7426    const align = popupData.textAlign || 'center'
7427    const imgW = imageWidth || 40
7428
7429    let html = `<div class="${cls.containerClasses}${isHorizontal ? ' qb-popup-horizontal' : ''}${isTopImage ? ' qb-popup-has-top-image' : ''}${isTakeover ? ' qb-popup-takeover' : ''}${isCover ? ' qb-popup-cover' : ''}" data-qb-popup data-qb-image-position="${imagePosition || 'top'}" style="color: ${textColor || '#000'}; text-align: ${align}${isCover ? '; background-image: url("' + image + '"); background-size: cover; background-position: center' : ''}">`
7430
7431    if (isTopImage) {
7432      html += `<img class="${cls.imageClasses} qb-popup-image--top" data-qb-popup-part="image" src="${image}" alt="" style="max-height: ${imgW * 0.4}rem" />`
7433    }
7434
7435    if (image && imagePosition === 'left') {
7436      html += `<img class="${cls.imageClasses} qb-popup-image--side" data-qb-popup-part="image" src="${image}" alt="" style="width: ${imageWidth}%" />`
7437    }
7438
7439    html += `<div class="qb-popup-body">`
7440
7441    if (popupData.showLogo && popupData.logoUrl) {
7442      const logoAlign = align === 'left' ? 'flex-start' : align === 'right' ? 'flex-end' : 'center'
7443      html += `<img class="qb-popup-logo" data-qb-popup-part="logo" src="${popupData.logoUrl}" alt="" style="align-self: ${logoAlign}" />`
7444    }
7445
7446    html += `<h2 class="${cls.titleClasses}" data-qb-popup-part="title">${this._escapeHtml(title || '')}</h2>`
7447    html += `<div class="${cls.textClasses}" data-qb-popup-part="text">${text || ''}</div>`
7448
7449    if (hasGoal) {
7450      var formAlign = align === 'left' ? 'flex-start' : align === 'right' ? 'flex-end' : 'center'
7451      html += `<form class="qb-popup-form" data-qb-popup-part="form" style="align-items: ${formAlign}">`
7452
7453      if (goalType === '1') {
7454        html += `<input class="${cls.inputClasses}" data-qb-popup-part="input" type="email" id="popupNewsletterEmail" placeholder="${this.config?.modal?.inputPlaceholder ?? 'Email'}" required />`
7455      }
7456
7457      html += `<button class="${cls.buttonClasses}" data-qb-popup-part="button" type="submit" style="background-color: ${btnBgColor || '#000'}; color: ${btnTextColor || '#fff'}">${this._escapeHtml(btnText || '')}</button>`
7458      html += `</form>`
7459    }
7460
7461    if (popupData.finePrint) {
7462      html += `<div class="qb-popup-fine-print" data-qb-popup-part="fine-print">${this._escapeHtml(popupData.finePrint)}</div>`
7463    }
7464
7465    html += `</div>`
7466
7467    if (image && imagePosition === 'right') {
7468      html += `<img class="${cls.imageClasses} qb-popup-image--side" data-qb-popup-part="image" src="${image}" alt="" style="width: ${imageWidth}%" />`
7469    }
7470
7471    html += `</div>`
7472
7473    return html
7474  },
7475  _renderSuccess: function (popupData) {
7476    const { title, textGoal, textColor, image, imagePosition, imageWidth, showImageOnSuccess } = popupData
7477    const cls = this.config.style.popup
7478    const showImage = image && showImageOnSuccess
7479    const isHorizontal = showImage && (imagePosition === 'left' || imagePosition === 'right')
7480    const isTopImage = showImage && imagePosition === 'top'
7481    const isTakeover = showImage && imagePosition === 'background'
7482    const isCover = showImage && imagePosition === 'cover'
7483    const align = popupData.textAlign || 'center'
7484    const imgW = imageWidth || 40
7485
7486    let html = `<div class="${cls.containerClasses} qb-popup-success${isHorizontal ? ' qb-popup-horizontal' : ''}${isTopImage ? ' qb-popup-has-top-image' : ''}${isTakeover ? ' qb-popup-takeover' : ''}${isCover ? ' qb-popup-cover' : ''}" data-qb-popup data-qb-popup-state="success" data-qb-image-position="${imagePosition || 'top'}" style="color: ${textColor || '#000'}; text-align: ${align}${isCover ? ';
7486 background-image: url("' + image + '"); background-size: cover; background-position: center' : ''}">`
7487
7488    if (isTopImage) {
7489      html += `<img class="${cls.imageClasses} qb-popup-image--top" data-qb-popup-part="image" src="${image}" alt="" style="max-height: ${imgW * 0.4}rem" />`
7490    }
7491
7492    if (showImage && imagePosition === 'left') {
7493      html += `<img class="${cls.imageClasses} qb-popup-image--side" data-qb-popup-part="image" src="${image}" alt="" style="width: ${imageWidth}%" />`
7494    }
7495
7496    html += `<div class="qb-popup-body">`
7497
7498    html += `<div class="qb-popup-success-icon" data-qb-popup-part="success-icon">`
7499    html += `<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="24" cy="24" r="24" fill="currentColor" opacity="0.1"/><path d="M15 24.5L21 30.5L33 18.5" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`
7500    html += `</div>`
7501
7502    html += `<h2 class="${cls.titleClasses}" data-qb-popup-part="title">${this._escapeHtml(title || '')}</h2>`
7503    html += `<div class="${cls.textClasses}" data-qb-popup-part="text">${textGoal || ''}</div>`
7504
7505    html += `</div>`
7506
7507    if (showImage && imagePosition === 'right') {
7508      html += `<img class="${cls.imageClasses} qb-popup-image--side" data-qb-popup-part="image" src="${image}" alt="" style="width: ${imageWidth}%" />`
7509    }
7510
7511    html += `</div>`
7512
7513    return html
7514  },
7515  _escapeHtml: function (str) {
7516    const div = document.createElement('div')
7517    div.textContent = str
7518    return div.innerHTML
7519  },
7520  _attachSubmitHandler: function () {
7521    document.addEventListener('submit', (e) => {
7522      if (e.target.classList.contains('qb-popup-form')) {
7523        e.preventDefault()
7524        this.onButtonClick(e.target)
7525      }
7526    })
7527
7528    document.addEventListener('click', (e) => {
7529      var link = e.target.closest('[data-qb-popup-part="text"] a')
7530      if (link && this.data.popup) {
7531        var ppid = this.data.popup.ppid
7532        if (ppid) {
7533          sessionStorage.setItem('qb_popup_shown_' + ppid, '1')
7534          var formData = new FormData()
7535          formData.append('ppid', ppid)
7536          navigator.sendBeacon('/apps/storepopups/shown', formData)
7537        }
7538      }
7539    })
7540  },
7541  onButtonClick: function (form) {
7542    const { goal_type } = this?.data?.popup ?? {}
7543
7544    if (goal_type !== '1') {
7545      this._showSuccess()
7546      return
7547    }
7548
7549    const input = form.querySelector('input[data-qb-popup-part="input"]')
7550    const email = input.value.trim()
7551
7552    input.style.border = ''
7553    form.querySelector('[data-qb-popup-part="error"]')?.remove()
7554
7555    if (!email) {
7556      this._markInvalid(input)
7557      return
7558    }
7559
7560    const formData = new FormData()
7561    formData.append('email_address', email)
7562
7563    fetch('/apps/storepopups/add', { method: 'POST', body: formData })
7564      .then((res) => res.json())
7565      .then((data) => {
7566        if (data && data.success) {
7567          this._showSuccess()
7568        } else if (data && data.reason === 'invalid_email') {
7569          this._markInvalid(input)
7570        } else {
7571          this._showError(form)
7572        }
7573      })
7574      .catch(() => this._showError(form))
7575  },
7576  _markInvalid: function (input) {
7577    if (input) input.style.border = '1px solid red'
7578  },
7579  _showError: function (form) {
7580    let error = form.querySelector('[data-qb-popup-part="error"]')
7581
7582    if (!error) {
7583      error = document.createElement('div')
7584      error.className = 'qb-popup-error'
7585      error.setAttribute('data-qb-popup-part', 'error')
7586      form.appendChild(error)
7587    }
7588
7589    error.textContent =
7590      this?.data?.popup?.signup_error_text ||
7591      'The signup could not be completed. Please try again later.'
7592  },
7593  _showSuccess: function () {
7594    this._goalCompleted = true
7595    Alpine.store('modal').setPopupState(
7596      'success',
7597      (data, state) => this._render(data, state)
7598    )
7599  },
7600  _injectStyles: function () {
7601    if (this._cssInjected) return
7602    this._cssInjected = true
7603
7604    const style = document.createElement('style')
7605    style.setAttribute('data-qb-popup-styles', '')
7606    style.textContent = `
7607      :root {
7608        --qb-popup-radius: 1rem;
7609        --qb-popup-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
7610        --qb-popup-max-width: 28rem;
7611        --qb-popup-padding: 2rem 2.5rem;
7612        --qb-popup-enter-duration: 0.3s;
7613        --qb-popup-enter-easing: cubic-bezier(0.16, 1, 0.3, 1);
7614        --qb-popup-btn-radius: 0.5rem;
7615        --qb-popup-btn-padding: 0.75rem 2rem;
7616        --qb-popup-btn-font-weight: 600;
7617        --qb-popup-input-radius: 0.5rem;
7618        --qb-popup-input-padding: 0.75rem 1rem;
7619        --qb-popup-input-border: 1px solid #e5e7eb;
7620        --qb-popup-gap: 1.25rem;
7621        --qb-popup-title-size: clamp(1.5em, 4vw, 2em);
7622        --qb-popup-text-size: 1em;
7623        --qb-popup-content-enter: qb-popup-content-enter;
7624      }
7625
7626      .qb-popup-logo {
7627        max-height: 1.75rem;
7628        max-width: 8rem;
7629        object-fit: contain;
7630      }
7631
7632      /* Anchor popup font-size so children using em units do not inherit
7633         theme-level html font-size quirks (e.g. Charme sets html to 10px). */
7634      .qb-popup-container,
7635      .qb-popup-cover,
7636      .qb-popup-horizontal {
7637        font-size: 16px;
7638      }
7639
7640      .qb-popup-container {
7641        text-align: center;
7642        padding: var(--qb-popup-padding);
7643        animation-name: var(--qb-popup-content-enter);
7644        animation-duration: var(--qb-popup-enter-duration);
7645        animation-timing-function: var(--qb-popup-enter-easing);
7646        animation-fill-mode: both;
7647      }
7648
7649      .qb-popup-title {
7650        font-size: var(--qb-popup-title-size);
7651        font-weight: 700;
7652        line-height: 1.2;
7653        margin: 0;
7654      }
7655
7656      .qb-popup-text {
7657        font-size: var(--qb-popup-text-size);
7658        line-height: 1.6;
7659        margin: 0;
7660        opacity: 0.85;
7661      }
7662
7663      .qb-popup-text a { text-decoration: underline; }
7664      .qb-popup-text .fr-green { color: #38a169; }
7665      .qb-popup-text .fr-strong { font-weight: bold; }
7666
7667      .qb-popup-form {
7668        display: flex;
7669        flex-direction: column;
7670        gap: 0.75rem;
7671      }
7672
7673      .qb-popup-input {
7674        width: 100%;
7675        padding: var(--qb-popup-input-padding);
7676        border: var(--qb-popup-input-border);
7677        border-radius: var(--qb-popup-input-radius);
7678        font-size: 1em;
7679        text-align: inherit;
7680        background: rgba(0, 0, 0, 0.03);
7681        color: inherit;
7682        outline: none;
7683        transition: border-color 0.2s, box-shadow 0.2s;
7684        box-sizing: border-box;
7685      }
7686
7687      .qb-popup-input:focus {
7688        border-color: currentColor;
7689        box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05);
7690      }
7691
7692      .qb-popup-button {
7693        display: inline-block;
7694        padding: var(--qb-popup-btn-padding);
7695        border: none;
7696        border-radius: var(--qb-popup-btn-radius);
7697        font-size: 1em;
7698        font-weight: var(--qb-popup-btn-font-weight);
7699        cursor: pointer;
7700        transition: opacity 0.2s, transform 0.1s;
7701        line-height: 1.4;
7702      }
7703
7704      .qb-popup-button:hover {
7705        opacity: 0.85;
7706      }
7707
7708      .qb-popup-button:active {
7709        transform: scale(0.98);
7710      }
7711
7712      .qb-popup-body {
7713        display: flex;
7714        flex-direction: column;
7715        gap: var(--qb-popup-gap);
7716        flex: 1;
7717        min-width: 0;
7718      }
7719
7720      .qb-popup-image {
7721        width: 100%;
7722        border-radius: calc(var(--qb-popup-radius) - 0.25rem);
7723        margin-bottom: var(--qb-popup-gap);
7724        object-fit: contain;
7725        max-height: var(--qb-popup-image-max-height, none);
7726      }
7727
7728      .qb-popup-has-top-image {
7729        padding: 0;
7730      }
7731
7732      .qb-popup-image--top {
7733        width: 100%;
7734        display: block;
7735        border-radius: 0;
7736        margin-bottom: 0;
7737        object-fit: cover;
7738        max-height: none;
7739      }
7740
7741      .qb-popup-has-top-image .qb-popup-body {
7742        padding: var(--qb-popup-padding);
7743      }
7744
7745      .qb-popup-cover {
7746        position: relative;
7747        display: flex;
7748        align-items: center;
7749        justify-content: center;
7750        text-align: center;
7751        min-height: 16rem;
7752      }
7753
7754      .qb-popup-cover .qb-popup-body {
7755        position: relative;
7756        z-index: 1;
7757        padding: var(--qb-popup-padding);
7758        text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
7759      }
7760
7761      .qb-popup-horizontal {
7762        display: flex;
7763        padding: 0;
7764        gap: 0;
7765      }
7766
7767      .qb-popup-horizontal .qb-popup-body {
7768        padding: var(--qb-popup-padding);
7769        justify-content: center;
7770      }
7771
7772      .qb-popup-image--side {
7773        width: auto;
7774        margin: 0;
7775        border-radius: 0;
7776        flex-shrink: 0;
7777        object-fit: cover;
7778        max-height: none;
7779      }
7780
7781      .qb-popup-horizontal[data-qb-image-position="left"] .qb-popup-image--side {
7782        border-radius: calc(var(--qb-popup-radius) - 0.125rem) 0 0 calc(var(--qb-popup-radius) - 0.125rem);
7783      }
7784
7785      .qb-popup-horizontal[data-qb-image-position="right"] .qb-popup-image--side {
7786        border-radius: 0 calc(var(--qb-popup-radius) - 0.125rem) calc(var(--qb-popup-radius) - 0.125rem) 0;
7787      }
7788
7789      @media (max-width: 480px) {
7790        .qb-popup-horizontal {
7791          flex-direction: column;
7792          padding: 0;
7793        }
7794        .qb-popup-image--side {
7795          width: 100% !important;
7796          max-width: none !important;
7797          max-height: 12rem;
7798          order: -1;
7799          object-fit: cover;
7800          border-radius: 0 !important;
7801        }
7802        .qb-popup-horizontal .qb-popup-body {
7803          padding: var(--qb-popup-padding);
7804        }
7805      }
7806
7807      .qb-popup-success {
7808        animation-name: var(--qb-popup-content-enter);
7809        animation-duration: var(--qb-popup-enter-duration);
7810        animation-timing-function: var(--qb-popup-enter-easing);
7811        animation-fill-mode: both;
7812      }
7813
7814      .qb-popup-success-icon {
7815        margin-bottom: 1rem;
7816        color: #38a169;
7817      }
7818
7819      .qb-popup-success-icon svg {
7820        display: inline-block;
7821      }
7822
7823      .qb-popup-fine-print {
7824        font-size: 0.8125em;
7825        line-height: 1.4;
7826        opacity: 0.5;
7827        margin-top: 0.25rem;
7828        color: inherit;
7829      }
7830
7831      .qb-popup-fine-print a { text-decoration: underline; }
7832
7833      .qb-popup-error {
7834        font-size: 0.875em;
7835        color: #dc2626;
7836        margin-top: 0.25rem;
7837      }
7838
7839      @keyframes qb-popup-content-enter {
7840        from {
7841          opacity: 0;
7842          transform: translateY(8px);
7843        }
7844        to {
7845          opacity: 1;
7846          transform: translateY(0);
7847        }
7848      }
7849
7850      @media (max-width: 640px) {
7851        :root {
7852          --qb-popup-padding: 1.5rem 1.25rem;
7853          --qb-popup-max-width: 100%;
7854        }
7855      }
7856
7857      /* Minimize tab */
7858      .qb-popup-minimize-tab {
7859        position: fixed;
7860        bottom: 1rem;
7861        left: 50%;
7862        transform: translateX(-50%);
7863        z-index: 9999;
7864        display: flex;
7865        align-items: center;
7866        gap: 0.5rem;
7867        padding: 0.625rem 1.25rem;
7868        border-radius: 9999px;
7869        cursor: pointer;
7870        font-size: 0.875rem;
7871        font-weight: 600;
7872        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
7873        animation: qb-tab-slide-up 0.3s cubic-bezier(0.16, 1, 0.3, 1) both;
7874        border: none;
7875        font-family: inherit;
7876        line-height: 1;
7877        white-space: nowrap;
7878        max-width: calc(100% - 2rem);
7879      }
7880
7881      .qb-popup-minimize-tab:hover {
7882        filter: brightness(1.1);
7883      }
7884
7885      .qb-popup-minimize-tab--exit {
7886        animation: qb-tab-slide-down 0.2s ease forwards !important;
7887      }
7888
7889      .qb-popup-minimize-tab__chevron {
7890        display: flex;
7891        align-items: center;
7892        flex-shrink: 0;
7893      }
7894
7895      .qb-popup-minimize-tab__close {
7896        display: flex;
7897        align-items: center;
7898        justify-content: center;
7899        background: none;
7900        border: none;
7901        cursor: pointer;
7902        color: inherit;
7903        opacity: 0.6;
7904        padding: 0.125rem;
7905        margin-left: 0.25rem;
7906        flex-shrink: 0;
7907        transition: opacity 0.15s;
7908      }
7909
7910      .qb-popup-minimize-tab__close:hover {
7911        opacity: 1;
7912      }
7913
7914      @keyframes qb-tab-slide-up {
7915        from { transform: translateX(-50%) translateY(calc(100% + 1rem)); }
7916        to { transform: translateX(-50%) translateY(0); }
7917      }
7918
7919      @keyframes qb-tab-slide-down {
7920        from { transform: translateX(-50%) translateY(0); }
7921        to { transform: translateX(-50%) translateY(calc(100% + 1rem)); }
7922      }
7923    `
7924    document.head.prepend(style)
7925  },
7926})
7927</script>
7927<script type="text/javascript">window.registerApp({
7928  name: 'Free Shipping Timer',
7929  identifier: 'QuickbutikFreeShippingTimer',
7930  description: 'Show free shipping timer',
7931  version: '1.0.0',
7932  pages: ['list', 'start', 'product'],
7933  data: {},
7934  config: {},
7935  onMount: function ({ appData, page }) {
7936    console.debug(`${this.identifier}@onMount`)
7937
7938    if (appData?.freeshippingtimer) {
7939      setTimeout(() => {
7940        Alpine.effect(() => {
7941          const currentAmount = Alpine.store('cart').totalAmountRaw
7942          const previousAmount = this.data.amount
7943
7944          // Skip if cart data hasn't loaded yet (null = initial state)
7945          if (currentAmount === null) {
7946            return
7947          }
7948
7949          // Detect if this is initial load (previous was null/undefined) or a cart change
7950          const isInitialLoad = previousAmount === null || previousAmount === undefined
7951          const hasCartChanged = previousAmount !== currentAmount
7952
7953          // Update stored amount
7954          this.data.amount = currentAmount
7955
7956          // Show notification on initial load (if cart has items) or when cart changes
7957          if ((isInitialLoad && currentAmount > 0) || (!isInitialLoad && hasCartChanged)) {
7958            fetch(qs_store_url + '/apps/freeshippingtimer/fetch', {
7959              method: 'POST',
7960            })
7961              .then((response) => response.json())
7962              .then((response) => {
7963                if (response?.message) {
7964                  Alpine.store('notifications').addNotification({
7965                    type: 'info',
7966                    category: 'freeshippingtimer',
7967                    clearCategory: true,
7968                    message: response.message,
7969                    lifeTime: 5000,
7970                    backgroundColor: appData?.freeshippingtimer?.bg_color,
7971                    textColor: appData?.freeshippingtimer?.text_color,
7972                  })
7973                }
7974              })
7975          }
7976        })
7977      }, 2000)
7978    }
7979  },
7980})
7981</script>
7981<script type="text/javascript">window.registerApp({
7982  name: 'Product Sort',
7983  identifier: 'QuickbutikProductSort',
7984  description: 'Product sort',
7985  version: '1.1.1',
7986  pages: ['list'],
7987  data: {},
7988  config: {
7989    productElementSelector: '[data-qb-selector="product-item"]',
7990    sortElementTargetSelector: '[data-qb-selector="sort-container"]',
7991    styling: {
7992      sortElementClasses: '',
7993    },
7994  },
7995  onMount: function ({ appData, page }) {
7996    console.debug(`${this.identifier}@onMount`)
7997
7998    if (appData?.productsort) {
7999      if (appData.productsort?.sortoptions) {
8000        this.config.options = appData.productsort.sortoptions
8001      }
8002      
8003      if (this.config.options) {
8004        this.injectSortElement({appData})
8005      }
8006    }
8007  },
8008  injectSortElement: function ({ appData }) {
8009    const container = document.querySelector(
8010      this.config.sortElementTargetSelector
8011    )
8012
8013    if (container) {
8014      container.appendChild(this.renderSortElement({appData}))
8015    }
8016  },
8017  renderSortElement: function ({ appData }) {
8018    const sortElement = document.createElement('select')
8019
8020    if (this.config.styling.sortElementClasses) {
8021      sortElement.classList.add(
8022        ...this.config.styling.sortElementClasses.split(' ')
8023      )
8024    }
8025
8026    // Get sort param from URL
8027    var preselectedSort = new URL(location.href).searchParams.get('sort')
8028
8029    // If no sort param from URL, use category's default sorting
8030    if (preselectedSort === null && appData.productsort?.set_default != '') {
8031      var preselectedSort = appData.productsort?.set_default
8032    }
8033
8034    Object.keys(this.config.options).forEach((optionKey) => {
8035      const optionElement = document.createElement('option')
8036
8037      const option = {
8038        value: optionKey,
8039        label: this.config.options[optionKey],
8040      }
8041
8042      optionElement.value = option.value
8043      optionElement.textContent = option.label
8044
8045      // Select default option (from category or URL)
8046      if (option.value == preselectedSort) {
8047        optionElement.defaultSelected = true
8048      }
8049
8050      sortElement.appendChild(optionElement)
8051    })
8052
8053    sortElement.addEventListener('change', (event) => {
8054      this.onSort(event.target.value)
8055    })
8056
8057    return sortElement
8058  },
8059  onSort: function (sort) {
8060    
8061    // Redirect when paginating
8062    if (qs_store_apps_data.pagination.pagination_activated) {
8063      var url = new URL(location.href);
8064      url.searchParams.set('sort', sort);      
8065      url.searchParams.delete('page');
8066  
8067      window.location.href = url.href;
8068      
8069    // Normal on-page filtering without pagination
8070    } else {
8071      const sortBy = sort.split('_')[0]
8072      const sortDirection = sort.split('_')[1]
8073
8074      const containerElement = document.getElementById('productList')
8075      const childElements = Array.from(
8076        containerElement.querySelectorAll(this.config?.productElementSelector)
8077      )
8078  
8079      const bucket = []
8080  
8081      childElements.forEach((child) => {
8082        const sortValue = child.getAttribute(`data-${sortBy}`)
8083  
8084        bucket.push({
8085          element: child,
8086          sortValue: sortValue
8087        })
8088      })
8089  
8090      const isNumeric = sortBy.includes('price')
8091  
8092      const locale = document.querySelector('html').getAttribute('lang') || 'en'
8093  
8094      const sortedKeys = bucket.sort((a, b) => {
8095        return sortDirection === '1'
8096          ? a.sortValue.localeCompare(b.sortValue, locale, {
8097            numeric: isNumeric
8098          })
8099          : b.sortValue.localeCompare(a.sortValue, locale, {
8100            numeric: isNumeric
8101          })
8102      })
8103  
8104      const sortedElements = sortedKeys.map((item) => item.element)
8105  
8106      sortedElements.forEach((child) => containerElement.appendChild(child))
8107
8108    }
8109  },
8110
8111})
8112</script>
8112<script type="text/javascript">window.registerApp({
8113  name: "ProductWatch",
8114  identifier: "QuickbutikProductWatch",
8115  description: "Add products to watchlist",
8116  version: "1.0.0",
8117  pages: ["product"],
8118  data: {},
8119  config: {
8120    showProductWatchButton: true,
8121    productSelector: "[data-qb-selector='product']",
8122    button: {
8123      appsContainer: '[data-qb-selector="product-apps"]',
8124      insertPosition: "beforeend", // 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend'
8125      buttonTag: "a",
8126      buttonClasses:
8127        "flex-shrink-0 flex w-full flex-wrap items-center justify-center w-16 h-16 font-bold bg-white text-black border",
8128    },
8129  },
8130  onMount: function ({ appData, page }) {
8131    console.debug(`${this.identifier}@onMount`);
8132
8133    if (page === "product" && appData?.productwatchers) {
8134      this.data = appData?.productwatchers ?? {};
8135      const productId = document.querySelector(this.config.productSelector)
8136        ?.dataset?.productId;
8137
8138      if (productId) {
8139        this.insertWatchProductButton({ productId });
8140      }
8141    }
8142  },
8143  insertWatchProductButton: function ({ productId }) {
8144    const { appsContainer, buttonTag, buttonClasses, insertPosition } =
8145      this.config.button;
8146
8147    const { btn_text, btn_bg_color, btn_text_color } = this.data ?? {};
8148
8149    const { showProductWatchButton } = this.config;
8150
8151    if (!showProductWatchButton) {
8152      return;
8153    }
8154
8155    const template = document.createElement("template");
8156    template.setAttribute("x-if", "!available");
8157
8158    const watchProductButton = document.createElement(buttonTag);
8159
8160    watchProductButton.classList.add(...buttonClasses.split(" "));
8161    watchProductButton.innerHTML = btn_text;
8162
8163    watchProductButton.style.backgroundColor = btn_bg_color;
8164    watchProductButton.style.color = btn_text_color;
8165
8166    watchProductButton.setAttribute(
8167      "x-on:click",
8168      `window.apps.${this.identifier}.onButtonClick($data.variant?.id)`
8169    );
8170
8171    template.content.appendChild(watchProductButton);
8172
8173    let container = document.querySelector(appsContainer);
8174    if (container) {
8175      container.insertAdjacentElement(insertPosition, template);
8176    }
8177  },
8178  onButtonClick: function (variantId) {
8179    const { popup_bg_color } = this.data ?? {};
8180
8181    Alpine.store("modal").openModal(this.renderModal(variantId), {
8182      backgroundColor: popup_bg_color,
8183    });
8184
8185    setTimeout(() => {
8186      document.querySelector("#watchlistEmail").focus();
8187    }, 500);
8188  },
8189  renderModal: function (variantId) {
8190    const {
8191      popup_title,
8192      popup_subtitle,
8193      btn_text,
8194      popup_text_color,
8195      btn_text_color,
8196      btn_bg_color,
8197    } = this.data ?? {};
8198
8199    const form = document.createElement("form");
8200    form.classList.add();
8201    form.style.color = popup_text_color ?? "#000";
8202
8203    const h2 = document.createElement("h2");
8204
8205    const titleClasses = this.config?.modal?.titleClasses;
8206    if (titleClasses) {
8207      h2.classList.add(...titleClasses.split(" "));
8208    }
8209
8210    h2.textContent = popup_title;
8211    form.appendChild(h2);
8212
8213    const paragraph = document.createElement("p");
8214    paragraph.innerHTML = popup_subtitle;
8215    form.appendChild(paragraph);
8216
8217    const emailInput = document.createElement("input");
8218    emailInput.type = "email";
8219    emailInput.id = "watchlistEmail";
8220
8221    const inputClasses = this.config?.modal?.inputClasses;
8222    if (inputClasses) {
8223      emailInput.classList.add(...inputClasses.split(" "));
8224    }
8225
8226    emailInput.placeholder = this.config?.modal?.inputPlaceholder ?? "Email";
8227    form.appendChild(emailInput);
8228
8229    const submitButton = document.createElement("button");
8230
8231    const submitButtonClasses = this.config?.modal?.submitButtonClasses;
8232    if (submitButtonClasses) {
8233      submitButton.classList.add(...submitButtonClasses.split(" "));
8234    }
8235
8236    submitButton.style.backgroundColor = btn_bg_color ?? "#000";
8237    submitButton.style.color = btn_text_color ?? "#fff";
8238    submitButton.textContent = btn_text;
8239    submitButton.type = "submit";
8240
8241    form.appendChild(submitButton);
8242
8243    form.setAttribute(
8244      "x-on:submit.prevent",
8245      `window.apps.QuickbutikProductWatch.submitEmail(document.querySelector('#watchlistEmail').value, ${variantId})`
8246    );
8247
8248    return form.outerHTML;
8249  },
8250  submitEmail: function (email, variantId) {
8251    const productId = document.querySelector(this.config.productSelector)
8252      .dataset.productId;
8253
8254    const UNKNOWKN_ERROR_MESSAGE =
8255      this.config?.unknowknErrorMessage ??
8256      "<p>V&auml;nligen f&ouml;rs&ouml;
8256k igen senare.</p>";
8257
8258    const bodyFormData = new FormData();
8259    bodyFormData.set("email_address", email);
8260    bodyFormData.set("product_id", productId);
8261
8262    if (variantId) {
8263      bodyFormData.set("variant_id", variantId);
8264    }
8265
8266    fetch(
8267      this.config?.productPage?.addProductWatchUrl ??
8268        "/apps/productwatchers/add",
8269      {
8270        method: "POST",
8271        body: bodyFormData,
8272      }
8273    )
8274      .then((response) => response.json())
8275      .then((response) => {
8276        if (response?.success) {
8277          Alpine.store("modal").setContent(
8278            this.renderSuccessPage(response?.text_thankyou || "<p>Tack</p>")
8279          );
8280
8281          setTimeout(() => {
8282            Alpine.store("modal").closeModal();
8283          }, 3000);
8284        } else {
8285          console.error(response);
8286
8287          Alpine.store("modal").setContent(
8288            this.renderErrorPage(response?.text_error || UNKNOWKN_ERROR_MESSAGE)
8289          );
8290        }
8291      })
8292      .catch((error) => {
8293        console.error("Submit email error", error);
8294
8295        Alpine.store("modal").setContent(
8296          this.renderErrorPage(error?.text_error || UNKNOWKN_ERROR_MESSAGE)
8297        );
8298      });
8299  },
8300  renderSuccessPage: function (message) {
8301    const { popup_title, popup_text_color } = this.data ?? {};
8302
8303    const h2 = document.createElement("h2");
8304
8305    const titleClasses = this.config?.modal?.titleClasses;
8306    if (titleClasses) {
8307      h2.classList.add(...titleClasses.split(" "));
8308    }
8309
8310    h2.textContent = popup_title;
8311
8312    const paragraph = document.createElement("p");
8313    paragraph.innerHTML = message;
8314
8315    const container = document.createElement("div");
8316
8317    const containerClasses = this.config?.modal?.containerClasses;
8318    if (containerClasses) {
8319      container.classList.add(...containerClasses.split(" "));
8320    }
8321
8322    container.appendChild(h2);
8323    container.appendChild(paragraph);
8324    container.style.color = popup_text_color || "#000";
8325
8326    return container.outerHTML;
8327  },
8328  renderErrorPage: function (message) {
8329    const h2 = document.createElement("h2");
8330    h2.classList.add("font-heading", "text-5xl", "mb-4");
8331    h2.textContent = this.config?.modal?.errorTitle ?? "Something went wrong";
8332
8333    const paragraph = document.createElement("p");
8334    paragraph.innerHTML = message;
8335
8336    const container = document.createElement("div");
8337
8338    const containerClasses = this.config?.modal?.containerClasses;
8339    if (containerClasses) {
8340      container.classList.add(...containerClasses.split(" "));
8341    }
8342
8343    container.appendChild(h2);
8344    container.appendChild(paragraph);
8345
8346    return container.outerHTML;
8347  },
8348});
8349</script>
8349<script type="text/javascript">window.registerApp({
8350  name: 'Product Filter',
8351  identifier: 'QuickbutikProductFilter',
8352  description: 'Product filter',
8353  version: '2.0.0',
8354  pages: ['list'],
8355  data: {
8356    filters: {},
8357    products: [],
8358    initialized: false,
8359  },
8360
8361  // XSS Protection: Sanitize HTML content
8362  sanitizeHTML: function(html) {
8363    if (!html) return ''
8364    
8365    // Create a temporary div to parse and sanitize HTML
8366    const temp = document.createElement('div')
8367    temp.textContent = html
8368    return temp.innerHTML
8369  },
8370
8371  // XSS Protection: Safe HTML insertion
8372  safeSetHTML: function(element, html) {
8373    if (!element || !html) return
8374    
8375    // For trusted content (like SVG icons), we can use innerHTML
8376    // For user content, we sanitize it
8377    if (this.isTrustedContent(html)) {
8378      element.innerHTML = html
8379    } else {
8380      element.innerHTML = this.sanitizeHTML(html)
8381    }
8382  },
8383
8384  // Check if content is trusted (like SVG icons, static HTML)
8385  isTrustedContent: function(html) {
8386    // Trust SVG icons and static HTML templates
8387    return html.includes('<svg') || 
8388           html.includes('class=') && !html.includes('onclick') && 
8389           !html.includes('onload') && !html.includes('onerror') &&
8390           !html.includes('javascript:') && !html.includes('data:')
8391  },
8392  config: {
8393    filterElementTargetSelector: '[data-qb-selector="filter-container"]',
8394    styling: {
8395      filterElementClasses: '',
8396      classes: {
8397        // Main containers
8398        filterButtonContainer: 'pf-filter-button-container',
8399        filtersWrapper: 'pf-inline-filters-wrapper relative z-10 flex flex-col items-start',
8400        filtersContainer: 'pf-inline-filters flex flex-wrap items-start justify-start w-full',
8401        activeFiltersSection: 'pf-active-filters-section w-full',
8402        activeFiltersRow: 'pf-active-filters-row flex flex-wrap gap-2 items-start',
8403        activeFiltersContainer: 'pf-active-filters flex gap-2 items-center flex-1',
8404        
8405        // Filter dropdowns
8406        filterDropdown: 'pf-filter-dropdown relative min-w-48',
8407        filterButton: 'pf-filter-button relative flex items-center justify-between cursor-pointer border px-4 py-3 button-bg button-text button-py button-px button-border button-rounding',
8408        filterButtonText: 'pf-filter-button-text flex-1 text-left input-text whitespace-nowrap overflow-hidden text-ellipsis',
8409        filterButtonCount: 'pf-filter-button-count rounded-full px-2 py-2 text-center mr-2',
8410        filterButtonBadge: 'pf-filter-button-badge text-white text-xs rounded-full w-5 h-5 flex items-center justify-center ml-2',
8411        filterArrow: 'pf-filter-arrow ml-2 text-xs transition-transform duration-200',
8412        
8413        // Dropdown content
8414        dropdownContent: 'pf-filter-dropdown-content absolute top-full left-0 bg-white border border-gray-300 shadow-lg z-50 max-h-75 overflow-y-auto mt-1 hidden min-w-full w-max',
8415        
8416        // Filter options
8417        filterGroup: 'pf-filter-group pb-4',
8418        filterOption: 'pf-filter-option block p-0 transition-colors',
8419        filterOptionCheckbox: 'sr-only',
8420        filterOptionLabel: 'pf-filter-option-label flex items-center justify-between cursor-pointer px-4 py-1 relative transition-all whitespace-nowrap',
8421        
8422        // Active filter badges
8423        filterBadge: 'pf-filter-badge inline-flex items-center text-xs',
8424        filterBadgeRemove: 'pf-filter-badge-remove ml-2 inline-flex items-center justify-center cursor-pointer rounded-full text-sm transition-colors hover:bg-opacity-30',
8425        
8426        // Reset button
8427        resetButton: 'pf-reset-filters-btn cursor-pointer px-4 py-2 text-sm transition-all whitespace-nowrap',
8428        clearFiltersButton: 'pf-clear-filters-btn cursor-pointer px-4 py-2 text-sm font-medium transition-all whitespace-nowrap text-gray-600 hover:text-gray-800',
8429        
8430        // No results message
8431        noResults: 'pf-no-results',
8432        noResultsContainer: 'pf-no-results-container',
8433      },
8434    },
8435  },
8436  onMount: function ({ appData }) {
8437    // Cleanup any existing listeners before reinitializing
8438    if (this.data.initialized) {
8439      this.cleanup()
8440    }
8441    
8442    if (appData?.productfilters && appData.productfilters?.products) {
8443      this.config.filters = []
8444      this.data.products = appData.productfilters.products
8445      this.applyDataTagsToProducts()
8446
8447      // Ensure the data is available globally
8448      window.qs_store_apps = window.qs_store_apps || {}
8449      window.qs_store_apps.productfilters = appData.productfilters
8450      
8451      // Check if sidebar container exists, otherwise use drawer
8452      const sidebarContainer = document.querySelector('[data-qb-selector="filter-sidebar-container"]')
8453      if (sidebarContainer) {
8454        this.createSidebarFilters(appData.productfilters, sidebarContainer)
8455      } else {
8456        this.createDrawerFilters(appData.productfilters)
8457      }
8458
8459      // Add pageshow event listener only when we use client-side filtering
8460      if (this.shouldUseClientSideFiltering()) {
8461        this.handlePageShow = () => {
8462          const filterElements = document.querySelectorAll('[data-filter-id]')
8463          for (const element of filterElements) {
8464            const originalOnchange = element.onchange
8465            element.onchange = null
8466            element.onchange = originalOnchange
8467          }
8468        }
8469        window.addEventListener('pageshow', this.handlePageShow)
8470      }
8471      
8472      // Mark as initialized
8473      this.data.initialized = true
8474      
8475      // Add beforeunload cleanup to ensure listeners are removed on page navigation
8476      this.handleBeforeUnload = () => {
8477        this.cleanup()
8478      }
8479      window.addEventListener('beforeunload', this.handleBeforeUnload)
8480    }
8481  },
8482
8483  onUnmount: function () {
8484    // Cleanup event listeners when component is unmounted
8485    this.cleanup()
8486    this.data.initialized = false
8487  },
8488
8489  // Global cleanup function that can be called externally
8490  globalCleanup: function() {
8491    if (window.QuickbutikProductFilter && window.QuickbutikProductFilter.cleanup) {
8492      window.QuickbutikProductFilter.cleanup()
8493    }
8494  },
8495  
8496  // Helper function to determine if we should use client-side filtering
8497  // Returns true only if pagination is not activated
8498  shouldUseClientSideFiltering: function () {
8499    const pagination = qs_store_apps_data?.pagination || {}
8500    
8501    // If pagination is not activated, use client-side filtering
8502    if (!pagination.pagination_activated) {
8503      return true
8504    }
8505    
8506    // If pagination is activated, always use server-side filtering (reload page)
8507    return false
8508  },
8509  applyDataTagsToProducts: function () {
8510    // Index the product elements once instead of querying the document per product.
8511    const productElementsById = new Map()
8512
8513    document
8514      .querySelectorAll('[data-qb-selector="product-item"][data-product-id]')
8515      .forEach((element) => {
8516        const productId = element.getAttribute('data-product-id')
8517
8518        if (!productElementsById.has(productId)) {
8519          productElementsById.set(productId, element)
8520        }
8521      })
8522
8523    Object.keys(this.data.products).forEach((productKey) => {
8524      const productFilter = this.data.products[productKey]
8525      const productElement = productElementsById.get(String(productFilter.id))
8526
8527      if (productElement) {
8528        Object.keys(productFilter?.filters).forEach((filterKey) => {
8529          const filterData = productFilter.filters[filterKey]
8530          // Parse composite filter id to get filter group and value
8531          const compositeIdParts = filterData.id ? filterData.id.split('-') : []
8532          
8533          if (compositeIdParts.length === 2) {
8534            const actualFilterGroupId = compositeIdParts[0]
8535            const actualFilterValueId = compositeIdParts[1]
8536            
8537            // Set specific filter attribute: data-filter-${filterGroupId}-${filterValueId}
8538            productElement.setAttribute(
8539              `data-filter-${actualFilterGroupId}-${actualFilterValueId}`,
8540              '1'
8541            )
8542          }
8543        })
8544      }
8545
8546    })
8547  },
8548  injectFilter: function (renderedFilterElement) {
8549    const container = document.querySelector(
8550      this.config.filterElementTargetSelector
8551    )
8552
8553    if (container) {
8554      container.appendChild(renderedFilterElement)
8555    }
8556  },
8557  renderFilterElement: function (filter) {
8558    const filterElement = document.createElement('select')
8559    filterElement.setAttribute('data-filter-id', filter.id)
8560    filterElement.setAttribute('data-hide-on-soldout', filter.variant_outofstock_check)
8561
8562     const url = new URL(location.href)
8563     const preselectedFilter = url.searchParams.get('filter_' + filter.id);
8564
8565    if (this.config.styling.filterElementClasses) {
8566      filterElement.classList.add(
8567        ...this.config.styling.filterElementClasses.split(' ')
8568      )
8569    }
8570
8571    const options = [
8572      {
8573        id: '',
8574        title: `-- ${filter.title} --`,
8575      },
8576      ...filter.values,
8577    ]
8578
8579    options.forEach((option) => {
8580      const optionElement = document.createElement('option')
8581
8582      optionElement.value = option.id
8583      optionElement.textContent = option.title
8584
8585      // Select default option (from category or URL)
8586      if (option.id === preselectedFilter) {
8587        optionElement.defaultSelected = true
8588      }
8589
8590      filterElement.appendChild(optionElement)
8591    })
8592
8593    filterElement.addEventListener('change', (event) => {
8594      this.onFilter(event.target)
8595    })
8596
8597    return filterElement
8598  },
8599  onFilter: function (filter) {
8600
8601    // Use client-side filtering or redirect based on pagination state
8602    if (this.shouldUseClientSideFiltering()) {
8603      // Normal on-page filtering without pagination or with single page
8604      if (filter.value === '') {
8605        delete this.data.filters[filter.dataset.filterId]
8606      } else {
8607        this.data.filters[filter.dataset.filterId] = filter.value
8608      }
8609
8610      this.updateProductList()
8611    } else {
8612      // Redirect when paginating with multiple pages
8613      const url = new URL(location.href)
8614      if (filter.value === '') {
8615        url.searchParams.delete('filter_' + filter.dataset.filterId)
8616      } else {
8617        url.searchParams.set('filter_' + filter.dataset.filterId, filter.value)
8618      }
8619      url.searchParams.delete('page')
8620  
8621      window.location.href = url.href;
8622    }
8623
8624  },
8625  updateProductList: function () {
8626    const containerElement = document.getElementById('productList')
8627    const childElements = Array.from(
8628      containerElement.querySelectorAll('[data-qb-selector="product-item"]')
8629    )
8630
8631    childElements.forEach((child) => {
8632      let show = true
8633
8634      Object.keys(this.data.filters).forEach((filterKey) => {
8635        const targetDataKey = `filter-${filterKey}-${this.data.filters[filterKey]}`
8636
8637        if (!Object.keys(child.dataset).includes(targetDataKey)) {
8638          show = false
8639        }
8640      })
8641
8642      if (show) {
8643        child.classList.remove('hidden')
8644      } else {
8645        child.classList.add('hidden')
8646      }
8647    })
8648  },
8649
8650  // Drawer Filter System Functions
8651  createDrawerFilters: function (data) {
8652    if (!data || !data.filters) {
8653      return
8654    }
8655
8656    // Create the main drawer filter container
8657    const drawerContainer = this.renderDrawerFilterContainer(data)
8658    
8659    // Inject into the target container
8660    this.injectDrawerFilters(drawerContainer)
8661    
8662    // Create drawer elements directly in body for highest stacking context
8663    this.createDrawerElements(data)
8664    
8665    // Set up drawer filter functionality
8666    this.setupDrawerFilterHandlers(data)
8667  },
8668
8669  // Sidebar Filter System Functions
8670  createSidebarFilters: function (data, sidebarContainer) {
8671    if (!data || !data.filters) {
8672      return
8673    }
8674
8675    // Create sidebar filter content
8676    const sidebarContent = this.renderSidebarFilterContent(data)
8677    
8678    // Inject into the sidebar container
8679    sidebarContainer.appendChild(sidebarContent)
8680    
8681    // First create the drawer content (temporarily)
8682    this.createDrawerFilters(data)
8683    
8684    // Then populate the sidebar with the drawer content
8685    this.populateSidebarContent(data.filters)
8686    
8687    // Hide the filter button since we have sidebar
8688    this.hideFilterButton()
8689    
8690    // Set up sidebar filter functionality
8691    this.setupSidebarFilterHandlers(data)
8692  },
8693
8694  setupSidebarFilterHandlers: function (data) {
8695    // Initialize state variables
8696    this.currentSelections = this.parseUrlFilters()
8697    this.pendingSelections = { ...this.currentSelections }
8698    
8699    // Populate sidebar content
8700    this.populateSidebarContent(data.filters)
8701    
8702    // Set up event handlers
8703    this.setupSidebarCheckboxHandlers()
8704    this.setupSidebarActionButtons()
8705    
8706    // Apply initial selections from URL
8707    this.applyInitialSelections()
8708    this.updateActiveFiltersDisplay()
8709    
8710    // Apply filtering if we have selections and should use client-side filtering
8711    if (this.shouldUseClientSideFiltering() && Object.keys(this.currentSelections).length > 0) {
8712      this.applyClientSideFiltering()
8713    }
8714  },
8715
8716  renderDrawerFilterContainer: function (data) {
8717    const container = document.createElement('div')
8718    container.className = this.config.styling.classes.filtersWrapper
8719    
8720    // Add CSS styles
8721    const style = document.createElement('style')
8722    style.textContent = this.getDrawerFilterCSS()
8723    container.appendChild(style)
8724    
8725    // Create filter button container
8726    const buttonContainer = document.createElement('div')
8727    buttonContainer.className = this.config.styling.classes.filterButtonContainer
8728    buttonContainer.innerHTML = `
8729      <button id="pfFilterButton" class="${this.config.styling.classes.filterButton}" type="button" aria-expanded="false" aria-haspopup="dialog" aria-controls="pfFilterDrawer" aria-describedby="pfFilterDescription">
8730        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="pf-filter-icon" aria-hidden="true">
8731          <path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75" stroke="currentColor" stroke-width="1.5"/>
8732        </svg>
8733        <span class="flex items-center">
8734          ${data.i18n?.filter || 'Filtrera'}
8735          <span id="pfFilterButtonBadge" class="${this.config.styling.classes.filterButtonBadge}" style="display: none;" aria-label="Antal aktiva filter"></span>
8736        </span>
8737      </button>
8738      <div id="pfFilterDescription" class="sr-only">${data.i18n?.filterBy || 'Filtrera efter'} produkter. Klicka f&ouml;r att &ouml;ppna filteralternativ.</div>
8739      <div class="${this.config.styling.classes.activeFiltersContainer}" id="pfActiveFilters" role="region" aria-label="Aktiva filter" aria-live="polite" aria-atomic="true"></div>
8740      <div class="${this.config.styling.classes.resetButton}" id="pfResetAllFilters" style="display: none;" role="button" tabindex="0" aria-label="Rensa alla filter" aria-describedby="pfResetDescription">${data.i18n?.clearAllFilters || 'Rensa alla filter'}</div>
8741      <div id="pfResetDescription" class="sr-only">Tar bort alla aktiva filter och visar alla produkter.</div>
8742    `
8743    container.appendChild(buttonContainer)
8744    
8745    return container
8746  },
8747
8748  renderSidebarFilterContent: function (data) {
8749    const container = document.createElement('div')
8750    container.className = 'pf-sidebar-filters'
8751    
8752    // Add CSS styles
8753    const style = document.createElement('style')
8754    style.textContent = this.getSidebarFilterCSS()
8755    container.appendChild(style)
8756    
8757    // Create sidebar header
8758    const header = document.createElement('div')
8759    header.className = 'pf-sidebar-header mb-6'
8760    header.innerHTML = `
8761      <h3 class="text-lg font-semibold mb-4">${data.i18n?.filterBy || 'Filtrera efter'}</h3>
8762      <div class="pf-active-filters mb-4" id="pfActiveFilters" role="region" aria-label="Aktiva filter" aria-live="polite" aria-atomic="true"></div>
8763      <div class="pf-reset-all mb-4" id="pfResetAllFilters" style="display: none;" role="button" tabindex="0" aria-label="Rensa alla filter">${data.i18n?.clearAllFilters || 'Rensa alla filter'}</div>
8764    `
8765    container.appendChild(header)
8766    
8767    // Create filter content
8768    const content = document.createElement('div')
8769    content.className = 'pf-sidebar-content'
8770    content.id = 'pfSidebarContent'
8771    container.appendChild(content)
8772    
8773    return container
8774  },
8775
8776  populateSidebarContent: function (filters) {
8777    const sidebarContent = document.getElementById('pfSidebarContent')
8778    if (!sidebarContent) return
8779    
8780    // Use the existing populateDrawerContent function with sidebar-specific container
8781    this.populateDrawerContent(filters)
8782    
8783    // Move the content to sidebar container
8784    const drawerContent = document.getElementById('pfDrawerContent')
8785    if (drawerContent) {
8786      // XSS Protection: Use safe HTML transfer instead of direct innerHTML
8787      this.safeSetHTML(sidebarContent, drawerContent.innerHTML)
8788      drawerContent.innerHTML = ''
8789      
8790      // Set up accordion functionality for the sidebar content
8791      this.setupSidebarAccordion()
8792    }
8793  },
8794
8795  hideFilterButton: function () {
8796    // Hide the filter button and related elements when sidebar is active
8797    const filterButton = document.getElementById('pfFilterButton')
8798    const filterButtonContainer = document.querySelector('.pf-filter-button-container')
8799    const filtersWrapper = document.querySelector('.pf-inline-filters-wrapper')
8800    
8801    if (filterButton) {
8802      filterButton.style.display = 'none'
8803    }
8804    
8805    if (filterButtonContainer) {
8806      filterButtonContainer.style.display = 'none'
8807    }
8808    
8809    if (filtersWrapper) {
8810      filtersWrapper.style.display = 'none'
8811    }
8812  },
8813
8814  setupSidebarAccordion: function () {
8815    const sidebarContent = document.getElementById('pfSidebarContent')
8816    if (!sidebarContent) return
8817    
8818    // Add accordion functionality to sidebar filter group titles
8819    sidebarContent.querySelectorAll('.pf-filter-group-title').forEach(title => {
8820      title.addEventListener('click', function () {
8821        const optionsContainer = this.nextElementSibling
8822        const icon = this.querySelector('.pf-accordion-icon')
8823        const isExpanded = this.getAttribute('aria-expanded') === 'true'
8824        
8825        if (optionsContainer && optionsContainer.classList.contains('pf-filter-options-container')) {
8826          if (isExpanded) {
8827            // Collapse
8828            optionsContainer.style.display = 'none'
8829            this.setAttribute('aria-expanded', 'false')
8830            // Show plus icon (collapsed)
8831            if (icon) {
8832              icon.innerHTML = `
8833                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
8834                  <path d="M12 4.5v15m7.5-7.5h-15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
8835                </svg>
8836              `
8837            }
8838          } else {
8839            // Expand
8840            optionsContainer.style.display = 'block'
8841            this.setAttribute('aria-expanded', 'true')
8842            // Show minus icon (expanded)
8843            if (icon) {
8844              icon.innerHTML = `
8845                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
8846                  <path d="M5 12h14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
8847                </svg>
8848              `
8849            }
8850          }
8851        }
8852      })
8853    })
8854  },
8855
8856  setupSidebarCheckboxHandlers: function () {
8857    // Use the existing drawer checkbox handler logic
8858    this.setupDrawerCheckboxHandlers()
8859  },
8860
8861  setupSidebarActionButtons: function () {
8862    // Use the existing drawer action buttons logic
8863    this.setupDrawerActionButtons()
8864  },
8865
8866  resetSidebarFilters: function () {
8867    // Use the existing drawer reset logic
8868    this.resetDrawerFilters()
8869  },
8870
8871  getSidebarFilterCSS: function () {
8872    return `
8873      /* Sidebar-specific overrides - leverages existing drawer CSS */
8874      .pf-sidebar-filters {
8875        background: white;
8876        border-radius: 8px;
8877        padding: 20px;
8878      }
8879      
8880      .pf-sidebar-header h3 {
8881        margin: 0 0 16px 0;
8882        font-size: 18px;
8883        font-weight: 600;
8884        color: #333;
8885      }
8886      
8887      /* Override drawer styles for sidebar */
8888      .pf-sidebar-filters .pf-filter-group {
8889        margin-bottom: 8px;
8890        padding-bottom: 8px;
8891      }
8892      
8893      .pf-sidebar-filters .pf-filter-group:last-child {
8894        border-bottom: none;
8895        margin-bottom: 0;
8896      }
8897      
8898      .pf-sidebar-filters .pf-filter-group-title {
8899        font-size: 14px;
8900        padding: 8px 0;
8901      }
8902      
8903      .pf-sidebar-filters .pf-filter-option {
8904        padding: 0.25rem 1rem;
8905      }
8906      
8907      .pf-sidebar-filters .pf-filter-options-container {
8908        padding-top: 0.5rem;
8909      }
8910      
8911      .pf-sidebar-filters .pf-accordion-icon {
8912        width: 20px;
8913        height: 20px;
8914        margin-left: 8px;
8915      }
8916      
8917      /* Handle long checkbox labels with proper text wrapping */
8918      .pf-sidebar-filters .pf-filter-option label {
8919        white-space: normal;
8920        word-wrap: break-word;
8921        overflow-wrap: break-word;
8922        hyphens: auto;
8923        line-height: 1.4;
8924        padding-right: 8px;
8925      }
8926    `
8927  },
8928
8929  createDrawerElements: function (data) {
8930    // Create drawer HTML with backdrop
8931    const drawerHTML = `
8932      <div id="pfDrawerBackdrop" class="pf-drawer-backdrop" role="presentation" aria-hidden="true"></div>
8933      <div id="pfFilterDrawer" class="pf-drawer" role="dialog" aria-modal="true" aria-labelledby="pfDrawerTitle" aria-describedby="pfDrawerDescription" aria-hidden="true">
8934        <div class="pf-drawer-header">
8935          <h3 id="pfDrawerTitle">${data.i18n?.filterBy || 'Filtrera efter'}</h3>
8936          <div id="pfDrawerDescription" class="sr-only">V&auml;lj filteralternativ f&ouml;r att begr&auml;nsa produktlistan. Anv&auml;nd tab f&ouml;r att navigera mellan alternativen.</div>
8937          <button id="pfCloseFilterDrawerBtn" class="pf-drawer-close-btn" aria-label="St&auml;ng filterdialog" type="button">
8938            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
8939              <path d="M6 18L18 6M6 6l12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
8940            </svg>
8941          </button>
8942        </div>
8943        <div id="pfDrawerContent" class="pf-drawer-content" role="region" aria-label="Filteralternativ" aria-live="polite">
8944          <!-- Filter options will be populated here -->
8945        </div>
8946        <div class="pf-drawer-footer">
8947          <button id="pfClearFiltersBtn" class="${this.config.styling.classes.clearFiltersButton}" type="button" aria-describedby="pfClearDescription">${data.i18n?.clearAllFilters || 'Rensa alla filter'}</button>
8948          <div id="pfClearDescription" class="sr-only">Tar bort alla valda filter och &aring;terst&auml;ller till ursprungsl&auml;ge.</div>
8949          <button id="pfApplyFiltersBtn" class="pf-drawer-apply-btn" type="button" aria-describedby="pfApplyDescription">${data.i18n?.showProducts || 'Visa produkter'}</button>
8950          <div id="pfApplyDescription" class="sr-only">Till&auml;mpar valda filter och visar matchande produkter.</div>
8951        </div>
8952      </div>
8953    `
8954    
8955    // Create a temporary div to parse the HTML
8956    const tempDiv = document.createElement('div')
8957    tempDiv.innerHTML = drawerHTML
8958    
8959    // Append both backdrop and drawer directly to body for highest stacking context
8960    const backdrop = tempDiv.querySelector('#pfDrawerBackdrop')
8961    const drawer = tempDiv.querySelector('#pfFilterDrawer')
8962    
8963    document.body.appendChild(backdrop)
8964    document.body.appendChild(drawer)
8965  },
8966
8967  populateDrawerContent: function (filters) {
8968    const drawerContent = document.getElementById('pfDrawerContent')
8969    if (!drawerContent) return
8970    
8971    drawerContent.innerHTML = ''
8972    
8973    // Count total filter options across all categories
8974    const totalFilterOptions = filters.reduce((total, filter) => total + filter.values.length, 0)
8975    const shouldExpandAll = totalFilterOptions <= 10
8976    // Auto-expand if there's only one filter (drawer mode only)
8977    const hasOnlyOneFilter = filters.length === 1
8978    
8979    filters.forEach(filter => {
8980      const groupDiv = document.createElement('div')
8981      groupDiv.className = this.config.styling.classes.filterGroup
8982      
8983      // Check if this filter group has any active selections
8984      const hasActiveSelections = this.currentSelections[filter.id] && this.currentSelections[filter.id].length > 0
8985      const shouldStartExpanded = shouldExpandAll || hasActiveSelections || hasOnlyOneFilter
8986      
8987      const titleElement = document.createElement('button')
8988      titleElement.className = 'pf-filter-group-title'
8989      titleElement.textContent = filter.title
8990      titleElement.setAttribute('aria-expanded', shouldStartExpanded ? 'true' : 'false')
8991      titleElement.setAttribute('aria-controls', `pf-filter-group-${filter.id}`)
8992      titleElement.setAttribute('type', 'button')
8993      
8994      const iconSpan = document.createElement('span')
8995      iconSpan.className = 'pf-accordion-icon'
8996      iconSpan.innerHTML = shouldStartExpanded ? `
8997        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
8998          <path d="M5 12h14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
8999        </svg>
9000      ` : `
9001        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
9002          <path d="M12 4.5v15m7.5-7.5h-15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
9003        </svg>
9004      `
9005      titleElement.appendChild(iconSpan)
9006      
9007      groupDiv.appendChild(titleElement)
9008      
9009      const optionsContainer = document.createElement('div')
9010      optionsContainer.className = 'pf-filter-options-container'
9011      optionsContainer.id = `pf-filter-group-${filter.id}`
9012      optionsContainer.style.display = shouldStartExpanded ? 'block' : 'none'
9013    
9014    filter.values.forEach(value => {
9015        const optionDiv = document.createElement('div')
9016        optionDiv.className = this.config.styling.classes.filterOption
9017      
9018        const checkbox = document.createElement('input')
9019        checkbox.type = 'checkbox'
9020        checkbox.className = this.config.styling.classes.filterOptionCheckbox
9021        checkbox.id = `pf-filter-${filter.id}-${value.id}`
9022        checkbox.value = value.id
9023        checkbox.dataset.filterId = filter.id
9024        checkbox.dataset.filterValueTitle = value.title
9025        checkbox.setAttribute('aria-describedby', `pf-filter-${filter.id}-${value.id}-description`)
9026        
9027        const label = document.createElement('label')
9028        label.className = this.config.styling.classes.filterOptionLabel
9029        label.htmlFor = checkbox.id
9030        label.textContent = value.title
9031        label.setAttribute('aria-hidden', 'true') // We'll use the checkbox's accessible name
9032        
9033        const description = document.createElement('div')
9034        description.id = `pf-filter-${filter.id}-${value.id}-description`
9035        description.className = 'sr-only'
9036        description.textContent = `V&auml;lj ${value.title} f&ouml;r att filtrera produkter i kategorin ${filter.title}`
9037      
9038        optionDiv.appendChild(checkbox)
9039        optionDiv.appendChild(label)
9040        optionDiv.appendChild(description)
9041        optionsContainer.appendChild(optionDiv)
9042      })
9043      
9044      groupDiv.appendChild(optionsContainer)
9045      drawerContent.appendChild(groupDiv)
9046    })
9047    
9048    // Add accordion functionality
9049    drawerContent.querySelectorAll('.pf-filter-group-title').forEach(title => {
9050      title.addEventListener('click', function () {
9051        const optionsContainer = this.nextElementSibling
9052        const icon = this.querySelector('.pf-accordion-icon')
9053        const isExpanded = this.getAttribute('aria-expanded') === 'true'
9054        
9055        if (optionsContainer && optionsContainer.classList.contains('pf-filter-options-container')) {
9056          if (isExpanded) {
9057            // Collapse
9058            optionsContainer.style.display = 'none'
9059            this.setAttribute('aria-expanded', 'false')
9060            // Show plus icon (collapsed)
9061            if (icon) {
9062              icon.innerHTML = `
9063                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
9064                  <path d="M12 4.5v15m7.5-7.5h-15" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
9065                </svg>
9066              `
9067            }
9068          } else {
9069            // Expand
9070            optionsContainer.style.display = 'block'
9071            this.setAttribute('aria-expanded', 'true')
9072            // Show minus icon (expanded)
9073            if (icon) {
9074              icon.innerHTML = `
9075                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
9076                  <path d="M5 12h14" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
9077                </svg>
9078              `
9079            }
9080          }
9081        }
9082      })
9083    })
9084  },
9085
9086  injectDrawerFilters: function (drawerContainer) {
9087    let container = document.querySelector(this.config.filterElementTargetSelector)
9088    
9089    if (container) {
9090      container.appendChild(drawerContainer)
9091      return
9092    }
9093    
9094    // Try common filter container selectors
9095    const fallbackSelectors = [
9096      '.filter-container',
9097      '.filters',
9098      '#filters',
9099      '.product-filters',
9100      '.category-filters',
9101      '[data-filter-container]',
9102      '.products-header',
9103      '.category-header'
9104    ]
9105    
9106    for (const selector of fallbackSelectors) {
9107      container = document.querySelector(selector)
9108      if (container) {
9109        container.appendChild(drawerContainer)
9110        return
9111      }
9112    }
9113    
9114    // Try to find a good spot before the product list
9115    const productList = document.querySelector('#productList, .product-list, .products, [data-qb-selector="product-list"]')
9116    if (productList) {
9117      productList.parentNode.insertBefore(drawerContainer, productList)
9118      return
9119    }
9120    
9121    // Last resort - inject at top of main content or body
9122    const main = document.querySelector('main, .main, .content, .page-content')
9123    if (main) {
9124      main.insertBefore(drawerContainer, main.firstChild)
9125    } else {
9126      document.body.insertBefore(drawerContainer, document.body.firstChild)
9127    }
9128  },
9129
9130  setupDrawerFilterHandlers: function (data) {
9131    // Initialize state variables
9132    this.currentSelections = this.parseUrlFilters()
9133    this.pendingSelections = { ...this.currentSelections }
9134    
9135    // Populate drawer content
9136    this.populateDrawerContent(data.filters)
9137    
9138    // Set up event handlers
9139    this.setupDrawerToggles()
9140    this.setupDrawerCheckboxHandlers()
9141    this.setupDrawerActionButtons()
9142    
9143    
9144    // Apply initial selections from URL
9145    this.applyInitialSelections()
9146    this.updateActiveFiltersDisplay()
9147    
9148    // Apply filtering if we have selections and should use client-side filtering
9149    if (this.shouldUseClientSideFiltering() && Object.keys(this.currentSelections).length > 0) {
9150      this.applyClientSideFiltering()
9151    }
9152  },
9153
9154  parseUrlFilters: function () {
9155    const urlParams = new URLSearchParams(window.location.search)
9156    const filters = {}
9157    
9158    for (const [key, value] of urlParams.entries()) {
9159      if (key.startsWith('filter_')) {
9160        const filterId = key.replace('filter_', '')
9161        filters[filterId] = value.split(',')
9162      }
9163    }
9164    
9165    return filters
9166  },
9167
9168
9169  setupDrawerToggles: function () {
9170    const openDrawerBtn = document.getElementById('pfFilterButton')
9171    const closeDrawerBtn = document.getElementById('pfCloseFilterDrawerBtn')
9172    const drawer = document.getElementById('pfFilterDrawer')
9173    const backdrop = document.getElementById('pfDrawerBackdrop')
9174    
9175    if (openDrawerBtn && drawer && backdrop) {
9176      openDrawerBtn.addEventListener('click', (event) => {
9177        event.stopPropagation()
9178        drawer.classList.add('pf-open')
9179        backdrop.classList.add('pf-open')
9180        document.body.classList.add('pf-drawer-open')
9181        
9182        // Update ARIA states
9183        openDrawerBtn.setAttribute('aria-expanded', 'true')
9184        drawer.setAttribute('aria-hidden', 'false')
9185        backdrop.setAttribute('aria-hidden', 'false')
9186        
9187        // Focus management - focus first interactive element in drawer
9188        setTimeout(() => {
9189          const firstFocusable = drawer.querySelector('button, input, [tabindex]:not
9189([tabindex="-1"])')
9190          if (firstFocusable) {
9191            firstFocusable.focus()
9192          }
9193        }, 100)
9194      })
9195    }
9196    
9197    if (closeDrawerBtn && drawer && backdrop) {
9198      closeDrawerBtn.addEventListener('click', (event) => {
9199        event.stopPropagation()
9200        this.closeDrawer()
9201      })
9202    }
9203    
9204    // Close drawer when clicking on backdrop
9205    if (backdrop) {
9206      backdrop.addEventListener('click', (event) => {
9207        event.stopPropagation()
9208        this.closeDrawer()
9209      })
9210    }
9211    
9212    // Close drawer when clicking outside
9213    this.handleDocumentClick = (event) => {
9214      if (drawer && drawer.classList.contains('pf-open') && 
9215          !drawer.contains(event.target) && 
9216          !openDrawerBtn?.contains(event.target)) {
9217        this.closeDrawer()
9218      }
9219    }
9220    document.addEventListener('click', this.handleDocumentClick)
9221    
9222    // Prevent drawer clicks from closing it
9223    if (drawer) {
9224      drawer.addEventListener('click', (event) => {
9225        event.stopPropagation()
9226      })
9227    }
9228    
9229    // Add keyboard navigation support
9230    this.handleDocumentKeydown = (event) => {
9231      if (drawer && drawer.classList.contains('pf-open')) {
9232        // Close drawer with Escape key
9233        if (event.key === 'Escape') {
9234          this.closeDrawer()
9235        }
9236        
9237        // Trap focus within drawer when open
9238        if (event.key === 'Tab') {
9239          const focusableElements = drawer.querySelectorAll(
9240            'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
9241          )
9242          const firstFocusable = focusableElements[0]
9243          const lastFocusable = focusableElements[focusableElements.length - 1]
9244          
9245          if (event.shiftKey) {
9246            // Shift + Tab
9247            if (document.activeElement === firstFocusable) {
9248              event.preventDefault()
9249              lastFocusable.focus()
9250            }
9251          } else {
9252            // Tab
9253            if (document.activeElement === lastFocusable) {
9254              event.preventDefault()
9255              firstFocusable.focus()
9256            }
9257          }
9258        }
9259      }
9260    }
9261    document.addEventListener('keydown', this.handleDocumentKeydown)
9262  },
9263
9264  setupDrawerCheckboxHandlers: function () {
9265    this.handleCheckboxChange = (event) => {
9266      if (event.target.matches('input[type="checkbox"]') && event.target.closest('.pf-filter-option')) {
9267        const checkbox = event.target
9268        const filterId = checkbox.getAttribute('data-filter-id')
9269        const value = checkbox.value
9270        
9271        if (!this.pendingSelections[filterId]) {
9272          this.pendingSelections[filterId] = []
9273        }
9274        
9275        if (checkbox.checked) {
9276          if (!this.pendingSelections[filterId].includes(value)) {
9277            this.pendingSelections[filterId].push(value)
9278          }
9279        } else {
9280          this.pendingSelections[filterId] = this.pendingSelections[filterId].filter(v => v !== value)
9281          if (this.pendingSelections[filterId].length === 0) {
9282            delete this.pendingSelections[filterId]
9283          }
9284        }
9285        
9286        // Check if we're in sidebar mode (immediate filtering) or drawer mode (pending selections)
9287        const isSidebar = event.target.closest('#pfSidebarContent')
9288        
9289        if (isSidebar || this.shouldUseClientSideFiltering()) {
9290          // Sidebar or client-side filtering: apply immediately
9291          this.currentSelections = { ...this.pendingSelections }
9292          this.updateActiveFiltersDisplay()
9293          this.updateUrlAndFilter()
9294        }
9295        // Otherwise, for drawer design with pagination (multiple pages), only update pending selections
9296        // and wait for "Visa produkter" button
9297      }
9298    }
9299    document.addEventListener('change', this.handleCheckboxChange)
9300  },
9301
9302  setupDrawerActionButtons: function () {
9303    // Use a small delay to ensure DOM elements are ready
9304    setTimeout(() => {
9305      const applyBtn = document.getElementById('pfApplyFiltersBtn')
9306      const resetBtn = document.getElementById('pfResetAllFilters')
9307      const clearBtn = document.getElementById('pfClearFiltersBtn')
9308      
9309      if (applyBtn) {
9310        applyBtn.addEventListener('click', (event) => {
9311        event.preventDefault()
9312        event.stopPropagation()
9313          this.applyDrawerFilters()
9314        })
9315      } else {
9316        console.warn('Apply filters button not found')
9317      }
9318      
9319      if (resetBtn) {
9320        resetBtn.addEventListener('click', (event) => {
9321          event.preventDefault()
9322          event.stopPropagation()
9323          this.resetDrawerFilters()
9324        })
9325      } else {
9326        console.warn('Reset filters button not found')
9327      }
9328      
9329      if (clearBtn) {
9330        clearBtn.addEventListener('click', (event) => {
9331          event.preventDefault()
9332          event.stopPropagation()
9333          this.clearDrawerFilters()
9334        })
9335      } else {
9336        console.warn('Clear filters button not found')
9337      }
9338    }, 100)
9339    
9340    // Keep the delegated event handler for filter badge removal
9341    document.addEventListener('click', (event) => {
9342      // Remove individual filter badge
9343      if (event.target.matches('.pf-filter-badge-remove') || event.target.closest('.pf-filter-badge-remove')) {
9344        const removeBtn = event.target.matches('.pf-filter-badge-remove') ? event.target : event.target.closest('.pf-filter-badge-remove')
9345        const filterId = removeBtn.getAttribute('data-filter-id')
9346        const value = removeBtn.getAttribute('data-filter-value')
9347        this.removeFilterValue(filterId, value)
9348        return
9349      }
9350    })
9351  },
9352
9353
9354
9355  resetDrawerFilters: function () {
9356    this.currentSelections = {}
9357    this.pendingSelections = {}
9358    
9359    // Uncheck all checkboxes
9360    const checkboxes = document.querySelectorAll('input[type="checkbox"]:checked')
9361    checkboxes.forEach(checkbox => {
9362      checkbox.checked = false
9363    })
9364    
9365    this.updateActiveFiltersDisplay()
9366    this.updateUrlAndFilterForClear()
9367    this.closeDrawer()
9368  },
9369
9370  clearDrawerFilters: function () {
9371    // Clear pending selections (what's currently selected in the drawer)
9372    this.pendingSelections = {}
9373    
9374    // Uncheck all checkboxes in the drawer
9375    const checkboxes = document.querySelectorAll('#pfFilterDrawer input[type="checkbox"]:checked')
9376    checkboxes.forEach(checkbox => {
9377      checkbox.checked = false
9378    })
9379    
9380    // Apply the cleared filters immediately
9381    this.currentSelections = {}
9382    this.updateActiveFiltersDisplay()
9383    this.updateUrlAndFilterForClear()
9384    this.closeDrawer()
9385  },
9386
9387  applyDrawerFilters: function () {
9388    // Apply all pending selections
9389    this.currentSelections = { ...this.pendingSelections }
9390    this.updateActiveFiltersDisplay()
9391    this.updateUrlAndFilter()
9392    
9393    // Apply client-side filtering if we should use it
9394    if (this.shouldUseClientSideFiltering()) {
9395      this.applyClientSideFiltering()
9396    }
9397    
9398    this.closeDrawer()
9399  },
9400
9401  closeDrawer: function () {
9402    const drawer = document.getElementById('pfFilterDrawer')
9403    const backdrop = document.getElementById('pfDrawerBackdrop')
9404    const openDrawerBtn = document.getElementById('pfFilterButton')
9405    
9406    if (drawer) {
9407      drawer.classList.remove('pf-open')
9408    }
9409    
9410    if (backdrop) {
9411      backdrop.classList.remove('pf-open')
9412    }
9413    
9414    document.body.classList.remove('pf-drawer-open')
9415    
9416    // Update ARIA states
9417    if (openDrawerBtn) {
9418      openDrawerBtn.setAttribute('aria-expanded', 'false')
9419    }
9420    if (drawer) {
9421      drawer.setAttribute('aria-hidden', 'true')
9422    }
9423    if (backdrop) {
9424      backdrop.setAttribute('aria-hidden', 'true')
9425    }
9426    
9427    // Return focus to the filter button
9428    if (openDrawerBtn) {
9429      openDrawerBtn.focus()
9430    }
9431  },
9432
9433
9434  updateUrlAndFilter: function () {
9435    const url = new URL(location.href)
9436    
9437    // Clear existing filter params
9438    const paramKeys = []
9439    for (const key of url.searchParams.keys()) {
9440      if (key.startsWith('filter_')) {
9441        paramKeys.push(key)
9442      }
9443    }
9444    paramKeys.forEach(key => url.searchParams.delete(key))
9445    
9446    // Add current filter params
9447    Object.keys(this.currentSelections).forEach(filterId => {
9448      const values = this.currentSelections[filterId]
9449      if (values && values.length > 0) {
9450        url.searchParams.set('filter_' + filterId, values.join(','))
9451      }
9452    })
9453    
9454    url.searchParams.delete('page')
9455    
9456    // Handle pagination vs client-side filtering
9457    if (this.shouldUseClientSideFiltering()) {
9458      history.pushState({}, '', url.toString())
9459      this.applyClientSideFiltering()
9460    } else {
9461      window.location.href = url.href
9462    }
9463  },
9464
9465  updateUrlAndFilterForClear: function () {
9466    const url = new URL(location.href)
9467    
9468    // Clear all filter params
9469    const paramKeys = []
9470    for (const key of url.searchParams.keys()) {
9471      if (key.startsWith('filter_')) {
9472        paramKeys.push(key)
9473      }
9474    }
9475    paramKeys.forEach(key => url.searchParams.delete(key))
9476    
9477    url.searchParams.delete('page')
9478    
9479    // When clearing filters, always reload if pagination is activated
9480    // because clearing filters might change the total number of products and pages
9481    const pagination = qs_store_apps_data?.pagination || {}
9482    if (pagination.pagination_activated) {
9483      // Always reload page when pagination is activated and filters are cleared
9484      window.location.href = url.href
9485    } else {
9486      // Use client-side filtering when pagination is not activated
9487      history.pushState({}, '', url.toString())
9488      this.applyClientSideFiltering()
9489    }
9490  },
9491
9492  applyClientSideFiltering: function () {
9493    // Since products are directly in the page, find them directly
9494    const productElements = document.querySelectorAll('[data-qb-selector="product-item"]')
9495    
9496    if (productElements.length === 0) {
9497      return
9498    }
9499
9500    productElements.forEach((productElement) => {
9501      let shouldShow = true
9502
9503      // Check each filter category
9504      Object.keys(this.currentSelections).forEach(filterId => {
9505        const selectedValues = this.currentSelections[filterId]
9506        if (!selectedValues || selectedValues.length === 0) {
9507          return // Skip empty filters
9508        }
9509
9510        // Check if product has any of the selected values for this filter
9511        let hasMatchingValue = false
9512        selectedValues.forEach(value => {
9513          const dataAttribute = `data-filter-${filterId}-${value}`
9514          if (productElement.hasAttribute(dataAttribute)) {
9515            hasMatchingValue = true
9516          }
9517        })
9518
9519        // If no matching value found for this required filter, hide the product
9520        if (!hasMatchingValue) {
9521          shouldShow = false
9522        }
9523      })
9524
9525      // Show/hide the product
9526      if (shouldShow) {
9527        productElement.classList.remove('hidden')
9528        productElement.style.display = '' // Also clear any inline style
9529      } else {
9530        productElement.classList.add('hidden')
9531        productElement.style.display = 'none' // Force hide with inline style
9532      }
9533    })
9534
9535    // Check if any products are visible
9536    const visibleProducts = document.querySelectorAll('[data-qb-selector="product-item"]:not(.hidden)')
9537    if (visibleProducts.length === 0) {
9538      this.showNoResultsMessage()
9539    } else {
9540      // Hide any existing "no results" message
9541      const existingNoResults = document.querySelector(`.${this.config.styling.classes.noResults}`)
9542      if (existingNoResults) {
9543        existingNoResults.remove()
9544      }
9545    }
9546  },
9547
9548  showNoResultsMessage: function () {
9549    // Remove any existing "no results" message
9550    const existingNoResults = document.querySelector(`.${this.config.styling.classes.noResults}`)
9551    if (existingNoResults) {
9552      existingNoResults.remove()
9553    }
9554
9555    // Find the product container
9556    const productContainer = document.querySelector('#productList, .product-list, .products, [data-qb-selector="product-list"]')
9557    if (!productContainer) return
9558
9559    // Get translation data
9560    const data = window.qs_store_apps?.productfilters || {}
9561
9562    // Create "no results" message
9563    const noResultsDiv = document.createElement('div')
9564    noResultsDiv.className = this.config.styling.classes.noResults
9565    noResultsDiv.innerHTML = `
9566      <div class="${this.config.styling.classes.noResultsContainer}">
9567        <h3>${data.i18n?.noResultsHeading || 'No products found'}</h3>
9568        <p>${data.i18n?.noResultsDescription || 'Try adjusting your filters to see more products.'}</p>
9569        <button onclick="window.apps.QuickbutikProductFilter.resetDrawerFilters()" class="pf-clear-filters-btn">${data.i18n?.clearAllFilters || 'Clear all filters'}</button>
9570      </div>
9571    `
9572
9573    // Insert the message after the product container
9574    productContainer.appendChild(noResultsDiv)
9575  },
9576
9577  updateActiveFiltersDisplay: function () {
9578    const container = document.getElementById('pfActiveFilters')
9579    if (!container) return
9580    
9581    // Clear all badges
9582    const badges = container.querySelectorAll('.pf-filter-badge')
9583    badges.forEach(badge => badge.remove())
9584    
9585    const data = window.qs_store_apps.productfilters
9586    let totalActiveFilters = 0
9587    
9588    Object.keys(this.currentSelections).forEach(filterId => {
9589      const values = this.currentSelections[filterId]
9590      if (!values || values.length === 0) return
9591      
9592      const filterData = data.filters.find(f => f.id === filterId)
9593      if (!filterData) return
9594      
9595      values.forEach(value => {
9596        const valueData = filterData.values.find(v => v.id === value)
9597        if (!valueData) return
9598        
9599        totalActiveFilters++
9600        
9601        const badge = document.createElement('div')
9602        badge.className = this.config.styling.classes.filterBadge
9603        badge.setAttribute('role', 'button')
9604        badge.setAttribute('tabindex', '0')
9605        // XSS Protection: Sanitize user content
9606        const sanitizedTitle = this.sanitizeHTML(valueData.title)
9607        badge.setAttribute('aria-label', `Ta bort filter: ${sanitizedTitle}`)
9608        badge.innerHTML = `
9609          <span>${sanitizedTitle}</span>
9610          <span class="${this.config.styling.classes.filterBadgeRemove}" data-filter-id="${filterId}" data-filter-value="${value}" role="button" tabindex="0" aria-label="Ta bort ${sanitizedTitle} filter">
9611            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
9612              <path d="M6 18L18 6M6 6l12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
9613            </svg>
9614          </span>
9615        `
9616          container.appendChild(badge)
9617      })
9618    })
9619    
9620    // Update filter button badge
9621    this.updateFilterButtonBadge(totalActiveFilters)
9622    
9623    // Show/hide reset button
9624    const resetBtn = document.getElementById('pfResetAllFilters')
9625    
9626    if (resetBtn) {
9627      const badgeCount = container.querySelectorAll('.pf-filter-badge').length
9628      const shouldShow = badgeCount > 0
9629      resetBtn.style.display = shouldShow ? 'block' : 'none'
9630    }
9631  },
9632
9633  updateFilterButtonBadge: function (count) {
9634    const badge = document.getElementById('pfFilterButtonBadge')
9635    if (!badge) return
9636    
9637    if (count > 0) {
9638      badge.textContent = count
9639      badge.style.display = 'flex'
9640      badge.setAttribute('aria-label', `${count} aktiva filter`)
9641    } else {
9642      badge.style.display = 'none'
9643    }
9644  },
9645
9646  applyInitialSelections: function () {
9647    Object.keys(this.currentSelections).forEach(filterId => {
9648      const values = this.currentSelections[filterId]
9649      
9650      values.forEach(value => {
9651        const checkboxId = `pf-filter-${filterId}-${value}`
9652        const checkbox = document.getElementById(checkboxId)
9653        if (checkbox) {
9654          checkbox.checked = true
9655        } else {
9656          console.warn(`Checkbox not found: ${checkboxId}`)
9657        }
9658      })
9659    })
9660    
9661    // Sync pending selections with current selections
9662    this.pendingSelections = { ...this.currentSelections }
9663  },
9664
9665
9666  removeFilterValue: function (filterId, value) {
9667    if (!this.currentSelections[filterId]) return
9668    
9669    this.currentSelections[filterId] = this.currentSelections[filterId].filter(v => v !== value)
9670    if (this.currentSelections[filterId].length === 0) {
9671      delete this.currentSelections[filterId]
9672    }
9673    
9674    // Update pending selections and UI
9675    this.pendingSelections = { ...this.currentSelections }
9676    this.updateActiveFiltersDisplay()
9677    this.updateUrlAndFilter()
9678    
9679    // Update checkbox
9680    const checkbox = document.getElementById(`pf-filter-${filterId}-${value}`)
9681    if (checkbox) {
9682      checkbox.checked = false
9683    }
9684  },
9685
9686  getDrawerFilterCSS: function () {
9687    return `
9688      /* Color variables for consistency */
9689      :root {
9690        --pf-primary-color: var(--color-primary, #000000);
9691        --pf-primary-dark: var(--color-primary, #000000);
9692        --pf-light-gray: var(--input-bg, var(--bg-header, #f8f8f8));
9693        --pf-border-color: var(--color-text, #e2e2e2);
9694        --pf-badge-bg: var(--button-bg, var(--color-primary, #000000));
9695        --pf-badge-border-color: var(--color-primary, #e2e2e2);
9696        --pf-text-dark: var(--color-text, var(--color-foreground, #333333));
9697        --pf-text-light: var(--button-text-primary, #ffffff);
9698        --pf-button-rounding: var(--button-rounding, 0.5rem);
9699        --pf-button-py: calc(var(--button-py, 1rem) * 0.75);
9700        --pf-button-px: calc(var(--button-px, 2rem) * 0.75);
9701        --pf-input-rounding: var(--input-rounding, 0.5rem);
9702        --pf-gap: var(--gap, 1rem);
9703        --pf-drawer-bg: var(--bg-card, var(--bg-body, #ffffff));
9704      }
9705
9706      /* Basic Reset & Body Style */
9707      body.pf-drawer-open {
9708        /* Allow scrolling when drawer is open */
9709      }
9710
9711      .pf-filter-button-container {
9712        display: flex;
9713        flex-wrap: wrap;
9714        align-items: center;
9715        gap: var(--pf-gap);
9716      }
9717
9718      .pf-filter-button {
9719        padding: var(--pf-button-py) var(--pf-button-px);
9720        cursor: pointer;
9721        background-color: var(--pf-light-gray);
9722        border-color: var(--pf-border-color);
9723        color: var(--pf-text-dark);
9724        border-radius: var(--pf-button-rounding);
9725        transition: all 0.2s ease;
9726        display: inline-flex;
9727        align-items: center;
9728        gap: 8px;
9729      }
9730
9731      .pf-filter-icon {
9732        flex-shrink: 0;
9733        opacity: 0.8;
9734      }
9735
9736      .pf-filter-button:hover {
9737        background-color: var(--pf-primary-color);
9738        color: var(--pf-text-light);
9739        border-color: var(--pf-primary-color);
9740      }
9741
9742      .pf-filter-button-badge {
9743        background-color: var(--pf-primary-color);
9744      }
9745
9746      /* Active filters display container */
9747      .pf-active-filters {
9748        display: flex;
9749        gap: var(--pf-gap);
9750        align-items: center;
9751      }
9752
9753      .pf-filter-badge {
9754        display: inline-flex;
9755        align-items: center;
9756        padding: var(--pf-button-py) var(--pf-button-px);
9757        border: 1px solid var(--pf-badge-border-color);
9758        background-color: var(--pf-badge-bg);
9759        color: white;
9760        border-radius: var(--pf-button-rounding);
9761        font-size: 1rem;
9762        font-weight: 500;
9763        transition: all 0.2s ease;
9764      }
9765
9766      .pf-filter-badge-remove {
9767        margin-left: 10px;
9768        width: 20px;
9769        height: 20px;
9770        display: inline-flex;
9771        align-items: center;
9772        justify-content: center;
9773        cursor: pointer;
9774        font-weight: bold;
9775        border-radius: 50%;
9776        transition: background-color 0.2s;
9777        background-color: rgba(255, 255, 255, 0.2);
9778        font-size: 1rem;
9779      }
9780
9781      .pf-filter-badge-remove:hover {
9782        background-color: rgba(255, 255, 255, 0.3);
9783      }
9784
9785      .pf-reset-all {
9786        cursor: pointer;
9787        color: var(--pf-text-dark);
9788        text-decoration: underline;
9789        font-size: 1rem;
9790        padding: 8px 14px;
9791        display: inline-flex;
9792        align-items: center;
9793        margin: 0;
9794        font-weight: 500;
9795        transition: color 0.2s;
9796        white-space: nowrap;
9797      }
9798
9799      .pf-reset-all:hover {
9800        color: var(--pf-primary-color);
9801      }
9802
9803      /* Hide when no active filters */
9804      .pf-active-filters:empty+.pf-reset-all {
9805        display: none;
9806      }
9807
9808      /* Drawer backdrop */
9809      .pf-drawer-backdrop {
9810        position: fixed;
9811        top: 0;
9812        left: 0;
9813        width: 100%;
9814        height: 100%;
9815        z-index: 9998;
9816        opacity: 0;
9817        visibility: hidden;
9818        transition: opacity 0.3s ease-in-out, visibility 0.3s ease-in-out;
9819      }
9820
9821      .pf-drawer-backdrop.pf-open {
9822        opacity: 1;
9823        visibility: visible;
9824      }
9825
9826      /* Drawer styling */
9827      .pf-drawer {
9828        position: fixed;
9829        top: 0;
9830        right: -350px;
9831        width: 350px;
9832        height: 100%;
9833        background-color: var(--pf-drawer-bg);
9834        box-shadow: -2px 0 10px rgba(0, 0, 0, 0.15);
9835        transition: right 0.3s ease-in-out;
9836        z-index: 9999;
9837        display: flex;
9838        flex-direction: column;
9839        overflow-x: hidden;
9840      }
9841
9842      .pf-drawer.pf-open {
9843        right: 0;
9844      }
9845
9846      .pf-drawer-header {
9847        padding: 16px;
9848        border-bottom: 1px solid var(--pf-border-color);
9849        display: flex;
9850        justify-content: space-between;
9851        align-items: center;
9852      }
9853
9854      .pf-drawer-header h3 {
9855        margin: 0;
9856        font-size: 1.2rem;
9857        font-weight: 600;
9858      }
9859
9860      .pf-drawer-close-btn {
9861        background: none;
9862        border: none;
9863        font-size: 1.5rem;
9864        cursor: pointer;
9865        width: 40px;
9866        height: 40px;
9867        display: flex;
9868        align-items: center;
9869        justify-content: center;
9870        border-radius: 50%;
9871        transition: background-color 0.2s;
9872        color: var(--pf-text-dark);
9873        font-weight: bold;
9874        line-height: 1;
9875      }
9876
9877      .pf-drawer-close-btn:hover {
9878        background-color: var(--pf-light-gray);
9879        color: var(--pf-primary-color);
9880      }
9881
9882      .pf-drawer-content {
9883        padding: 16px;
9884        overflow-y: auto;
9885        overflow-x: hidden;
9886        flex-grow: 1;
9887        max-width: 100%;
9888      }
9889
9890      .pf-drawer-footer {
9891        padding: 16px;
9892        border-top: 1px solid var(--pf-border-color);
9893        text-align: center;
9894        display: flex;
9895        flex-direction: column;
9896        gap: 12px;
9897      }
9898
9899      .pf-drawer-apply-btn {
9900        padding: 12px 20px;
9901        font-size: 1rem;
9902        cursor: pointer;
9903        background-color: var(--pf-primary-color);
9904        color: white;
9905        border: none;
9906        border-radius: 25px;
9907        width: 100%;
9908        font-weight: 500;
9909        transition: background-color 0.2s;
9910        box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
9911      }
9912
9913      .pf-drawer-apply-btn:hover {
9914        background-color: var(--pf-primary-dark);
9915      }
9916
9917      .pf-clear-filters-btn {
9918        background: none;
9919        border: none;
9920        color: var(--pf-text-dark);
9921        opacity: 0.7;
9922        transition: opacity 0.2s ease;
9923      }
9924
9925      .pf-clear-filters-btn:hover {
9926        opacity: 1;
9927        color: var(--pf-primary-color);
9928      }
9929
9930      /* Styling for filter groups and options in drawer */
9931      .pf-filter-group {
9932        border-bottom: 1px solid var(--pf-border-color);
9933      }
9934
9935      .pf-filter-group:last-child {
9936        border-bottom: none;
9937        margin-bottom: 0;
9938        padding-bottom: 0;
9939      }
9940
9941      .pf-filter-group-title {
9942        margin-top: 0;
9943        font-size: 1.1rem;
9944        padding: 10px 0;
9945        cursor: pointer;
9946        display: flex;
9947        justify-content: space-between;
9948        align-items: center;
9949        font-weight: 600;
9950        color: var(--pf-text-dark);
9951        border: none;
9952        background: none;
9953        width: 100%;
9954        text-align: left;
9955      }
9956
9957      .pf-accordion-icon {
9958        margin-left: 10px;
9959        width: 24px;
9960        height: 24px;
9961        display: flex;
9962        align-items: center;
9963        justify-content: center;
9964        border-radius: 50%;
9965        background-color: var(--pf-light-gray);
9966        color: var(--pf-text-dark);
9967        transition: background-color 0.2s ease;
9968      }
9969
9970      .pf-accordion-icon:hover {
9971        background-color: var(--pf-primary-color);
9972        color: white;
9973      }
9974
9975      .pf-accordion-icon svg {
9976        width: 16px;
9977        height: 16px;
9978      }
9979
9980      .pf-filter-options-container {
9981        padding-top: 0.5rem;
9982      }
9983
9984      /* Custom Styling for Filter Options (Checkboxes) */
9985      .pf-filter-option {
9986        display: block;
9987        position: relative;
9988        padding: 0.25rem 1rem;
9989        transition: background-color 0.15s ease-in-out;
9990      }
9991
9992      .pf-filter-option:hover {
9993        background-color: var(--pf-light-gray);
9994      }
9995
9996      .pf-filter-option input[type="checkbox"] {
9997        opacity: 0;
9998        position: absolute;
9999        width: 0;
10000        height: 0;
10001        cursor: pointer;
10002      }
10003
10004      .pf-filter-option label {
10005        display: flex;
10006        align-items: center;
10007        cursor: pointer;
10008        position: relative;
10009        padding-left: 32px;
10010        transition: font-weight 0.15s ease-in-out, color 0.15s ease-in-out;
10011      }
10012
10013      /* Custom Checkbox Box */
10014      .pf-filter-option label::before {
10015        content: '';
10016        position: absolute;
10017        left: 0;
10018        top: 50%;
10019        transform: translateY(-50%);
10020        width: 20px;
10021        height: 20px;
10022        border: 2px solid var(--pf-border-color);
10023        border-radius: 4px;
10024        background-color: var(--pf-input-bg, transparent);
10025        transition: border-color 0.15s ease-in-out, background-color 0.15s ease-in-out;
10026      }
10027
10028      /* Custom Checkmark */
10029      .pf-filter-option input[type="checkbox"]:checked+label::after {
10030        content: '';
10031        position: absolute;
10032        left: 7px;
10033        top: 50%;
10034        width: 7px;
10035        height: 14px;
10036        border-style: solid;
10037        border-color: #fff;
10038        border-width: 0 3px 3px 0;
10039        transform: translateY(-65%) rotate(45deg);
10040      }
10041
10042      /* When checkbox is checked: style the custom box and label text */
10043      .pf-filter-option input[type="checkbox"]:checked+label::before {
10044        background-color: var(--pf-primary-color);
10045        border-color: var(--pf-primary-color);
10046      }
10047
10048      .pf-filter-option input[type="checkbox"]:checked+label {
10049        font-weight: 600;
10050      }
10051      
10052      /* Focus style for accessibility */
10053      .pf-filter-option input[type="checkbox"]:focus-visible+label::before {
10054        box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
10055      }
10056
10057      /* Screen reader only class */
10058      .sr-only {
10059        position: absolute;
10060        width: 1px;
10061        height: 1px;
10062        padding: 0;
10063        margin: -1px;
10064        overflow: hidden;
10065        clip: rect(0, 0, 0, 0);
10066        white-space: nowrap;
10067        border: 0;
10068      }
10069
10070      /* Responsive adjustments for mobile */
10071      @media (max-width: 767px) {
10072        .pf-filter-button-container {
10073          flex-direction: column;
10074      }
10075      
10076      .pf-filter-button {
10077          text-align: center;
10078        }
10079
10080        .pf-active-filters {
10081          display: none;
10082        }
10083
10084        .pf-reset-all {
10085          text-align: center;
10086          font-weight: 500;
10087          padding: 10px;
10088          margin-top: 5px;
10089          border-top: 1px solid var(--pf-border-color);
10090        }
10091
10092        .pf-drawer {
10093          width: 100%;
10094          right: -100%;
10095        }
10096
10097        .pf-drawer-backdrop {
10098          z-index: 9998;
10099        }
10100      }
10101
10102      /* No results message - break out of grid and center full width */
10103      .pf-no-results {
10104        grid-column: 1 / -1; /* Span all grid columns */
10105        width: 100%;
10106        display: flex;
10107        justify-content: center;
10108        align-items: center;
10109        min-height: 200px;
10110      }
10111      
10112      .pf-no-results-container {
10113        text-align: center;
10114        padding: 40px 20px;
10115        max-width: 500px;
10116        width: 100%;
10117      }
10118      
10119      .pf-no-results-container h3 {
10120        margin: 0 0 10px 0;
10121        font-weight: 700;
10122      }
10123      
10124      .pf-no-results-container p {
10125        margin: 0 0 20px 0;
10126        line-height: 1.5;
10127      }
10128      
10129      .pf-no-results-container .pf-clear-filters-btn {
10130        background: var(--color-primary, #000000);
10131        color: white;
10132        border: none;
10133        padding: 10px 20px;
10134        border-radius: 4px;
10135        cursor: pointer;
10136        transition: background-color 0.2s ease;
10137      }
10138      
10139      .pf-no-results-container .pf-clear-filters-btn:hover {
10140        background: var(--color-primary, #000000);
10141      }
10142    `
10143  },
10144
10145  // Cleanup method to remove event listeners
10146  cleanup: function() {
10147    if (this.handleDocumentClick) {
10148      document.removeEventListener('click', this.handleDocumentClick)
10149      this.handleDocumentClick = null
10150    }
10151    if (this.handleDocumentKeydown) {
10152      document.removeEventListener('keydown', this.handleDocumentKeydown)
10153      this.handleDocumentKeydown = null
10154    }
10155    if (this.handlePageShow) {
10156      window.removeEventListener('pageshow', this.handlePageShow)
10157      this.handlePageShow = null
10158    }
10159    if (this.handleCheckboxChange) {
10160      document.removeEventListener('change', this.handleCheckboxChange)
10161      this.handleCheckboxChange = null
10162    }
10163    if (this.handleBeforeUnload) {
10164      window.removeEventListener('beforeunload', this.handleBeforeUnload)
10165      this.handleBeforeUnload = null
10166    }
10167  },
10168
10169})
10170
10171// Global cleanup function that can be called from anywhere
10172window.cleanupProductFilter = function() {
10173  if (window.QuickbutikProductFilter && window.QuickbutikProductFilter.cleanup) {
10174    window.QuickbutikProductFilter.cleanup()
10175  }
10176}</script>
10176<script type="text/javascript">window.registerApp({
10177  name: 'ProductProperties',
10178  identifier: 'QuickbutikProductProperties',
10179  description: 'Show product properties',
10180  version: '1.1.0',
10181  pages: ['product'],
10182  data: {},
10183  config: {
10184    insertTarget: {
10185      selector: '#product-properties',
10186      position: 'afterbegin', // beforebegin | afterbegin | beforeend | afterend
10187    },
10188    fieldsAttributes: {
10189      fileInputAccept: 'image/*,application/pdf',
10190    },
10191    styling: {
10192      formClasses: 'flex gap-4 flex-col pb-4',
10193      inputContainerClasses: 'flex flex-col gap-2',
10194      labelClasses: 'text-sm font-bold text-gray-700',
10195      labelErrorClasses: 'ml-1 text-red-500',
10196      checkboxInputClasses: 'form-checkbox h-5 w-5 text-gray-600',
10197      shortTextInputClasses:
10198        'block w-full p-3 text-xs text-black placeholder-gray-500 transition-all bg-white border border-gray-200 focus:border-gray-700 focus:outline-none',
10199      longTextInputClasses:
10200        'block w-full p-3 text-xs text-black placeholder-gray-500 transition-all bg-white border border-gray-200 focus:border-gray-700 focus:outline-none',
10201      fileContainerClasses: 'flex w-full input-bg input-border input-px input-py input-rounding w-full',
10202      fileInputClasses: 'input-bg input-border input-rounding input-px input-py',
10203      fileInputButtonClasses: 'shrink-0 cursor-pointer bg-primary text-center button-text-primary font-bold button-py button-px button-rounding mr-4 transition duration-100 hover:opacity-70',
10204      fileInputFilenameClasses: 'truncate button-py',
10205      fileUploadProgressContainerClasses: 'block relative w-full mt-2 rounded',
10206      fileUploadProgressBarBackgroundClasses: 'block absolute w-full h-full bg-black opacity-10 text-white rounded',
10207      fileUploadProgressBarClasses: 'block relative bg-primary py-1 px-4 text-xs font-bold text-white rounded',
10208      addonPriceClasses: 'text-sm text-gray-600 ml-0',
10209    },
10210  },
10211  onMount: function ({ appData, page }) {
10212    console.debug(`${this.identifier}@onMount`)
10213
10214    const productID = document
10215      .querySelector('[data-product-id]')
10216      .getAttribute('data-product-id')
10217
10218    if (productID) {
10219      fetch(`${qs_store_url}/apps/productproperties/view?product_id=${productID}`)
10220        .then((response) => response.json())
10221        .then((data) => {
10222          this.data = data
10223          this.renderProductProperties()
10224        })
10225        .catch((error) => {
10226          console.error('Error:', error);
10227        })
10228    }
10229
10230  },
10231  generageInputId: function (id) {
10232    return `ProductProperties-${id}`
10233  },
10234  generateInputName: function (id) {
10235    return `product_properties_${id}`
10236  },
10237  renderProductProperties: function () {
10238    const productPropertiesContainer = document.querySelector(this.data.identifier)
10239
10240    const insertPosition = {
10241      before: 'beforebegin',
10242      after: 'afterend',
10243      in: 'afterbegin',
10244    }[this.data.apply]
10245
10246    if (productPropertiesContainer && insertPosition && this.data.properties) {
10247      const form = document.createElement('form')
10248      form.classList.add(...this.config.styling.formClasses.split(' '))
10249      form.setAttribute('id', 'ProductPropertiesForm')
10250
10251      this.data.properties.forEach((property) => {
10252        const [key] =
10253          Object.entries(property)
10254            .filter(([key]) =>
10255              ['isCheckbox', 'isFile', 'isLongtext', 'isShorttext', 'isAddonProduct'].includes(
10256                key
10257              )
10258            )
10259            .find(([_, value]) => value) || []
10260
10261        let input = null
10262
10263        switch (key) {
10264          case 'isCheckbox':
10265            input = this.renderCheckbox(property)
10266            break
10267          case 'isFile':
10268            input = this.renderFile(property)
10269            break
10270          case 'isLongtext':
10271            input = this.renderLongtext(property)
10272            break
10273          case 'isShorttext':
10274            input = this.renderShorttext(property)
10275            break
10276          case 'isAddonProduct':
10277            input = this.renderAddonProduct(property)
10278            break
10279          default:
10280            console.log('No valid type found')
10281        }
10282
10283        if (input) {
10284          const inputContainer = document.createElement('div')
10285          inputContainer.classList.add(
10286            ...this.config.styling.inputContainerClasses.split(' ')
10287          )
10288          inputContainer.classList.add('product-property-container') // Add a class for easier selection
10289          form.appendChild(inputContainer)
10290
10291          // Only add label for non-addon products and checkboxes
10292          if (!['isAddonProduct', 'isCheckbox'].includes(key)) {
10293            
10294            // Label
10295            const label = document.createElement('label')
10296            label.classList.add(...this.config.styling.labelClasses.split(' '))
10297
10298            // File element
10299            if (key == 'isFile') {
10300              label.setAttribute('for', 'file-' + property.id)
10301            } else {
10302              label.setAttribute('for', this.generageInputId(property.id))
10303            }
10304            label.innerText = property.title
10305
10306            // Add addon price display next to the property title
10307            if (property.addonPriceRaw && parseFloat(property.addonPriceRaw) > 0) {
10308              const addonPriceSpan = document.createElement('span')
10309              addonPriceSpan.classList.add(...this.config.styling.addonPriceClasses.split(' '))
10310              addonPriceSpan.classList.add('addon-price') // Add a class for easier selection
10311              addonPriceSpan.innerText = ` (+${property.addonPrice})`
10312              addonPriceSpan.setAttribute('data-addon-price', property.addonPriceRaw)
10313              label.appendChild(addonPriceSpan)
10314            }
10315
10316            if (property.required === '1') {
10317              const requiredStar = document.createElement('span')
10318              requiredStar.classList.add(...this.config.styling.labelErrorClasses.split(' '))
10319              requiredStar.innerText = '*'
10320              label.appendChild(requiredStar)
10321            }
10322            inputContainer.appendChild(label)
10323          }
10324
10325          // All elements except files and addon products, which need specific structure and attributes
10326          if (key !== 'isFile' && key !== 'isAddonProduct') {
10327            input.setAttribute('id', this.generageInputId(property.id))
10328            input.setAttribute('name', this.generateInputName(property.id))
10329            input.setAttribute('data-qb-property', property.id)
10330
10331            if (property.required === '1') {
10332              input.setAttribute('required', true)
10333            }
10334          }
10335
10336          inputContainer.appendChild(input)
10337
10338          form.appendChild(inputContainer)
10339        }
10340      })
10341
10342      productPropertiesContainer.insertAdjacentElement(insertPosition, form)
10343
10344      // Only render ReCaptcha if we have a File property
10345      if (this.data.hasFileProperties) {
10346        this.renderReCaptcha()
10347      }
10348    }
10349  },
10350  renderCheckbox: function (property) {
10351    const checkboxWrapper = document.createElement('div')
10352    checkboxWrapper.classList.add('flex', 'items-center')
10353
10354    const checkbox = document.createElement('input')
10355    checkbox.classList.add(
10356      ...this.config.styling.checkboxInputClasses.split(' ')
10357    )
10358    checkbox.setAttribute('type', 'checkbox')
10359    checkbox.setAttribute('name', this.generateInputName(property.id))
10360    checkbox.setAttribute('value', '1')
10361    checkbox.setAttribute('data-qb-property', property.id)
10362
10363    // Label
10364    const label = document.createElement('label')
10365    label.classList.add(...this.config.styling.labelClasses.split(' '))
10366    label.classList.add('ml-4')
10367    label.setAttribute('for', this.generageInputId(property.id))
10368    label.innerText = property.title
10369
10370    if (property.required === '1') {
10371      const requiredStar = document.createElement('span')
10372      requiredStar.classList.add(...this.config.styling.labelErrorClasses.split(' '))
10373      requiredStar.innerText = '*'
10374      label.appendChild(requiredStar)
10375
10376      checkbox.setAttribute('required', true)
10377    }
10378
10379    checkboxWrapper.appendChild(checkbox)
10380    checkboxWrapper.appendChild(label)
10381
10382    return checkboxWrapper
10383  },
10384  renderAddonProduct: function (property) {
10385    const addonContainer = document.createElement('div')
10386    addonContainer.classList.add('addon-product-container', 'flex', 'items-center', 'p-4', 'border', 'rounded-md')
10387
10388    // Checkbox
10389    const checkbox = document.createElement('input')
10390    checkbox.classList.add(
10391      ...this.config.styling.checkboxInputClasses.split(' '),
10392      'mr-4'
10393    )
10394    checkbox.setAttribute('type', 'checkbox')
10395    checkbox.setAttribute('name', this.generateInputName(property.id))
10396    checkbox.setAttribute('value', '1')
10397    checkbox.setAttribute('data-qb-property', property.id)
10398
10399    if (property.required === '1') {
10400      checkbox.setAttribute('required', true)
10401    }
10402
10403    // Add checkbox to the container first
10404    addonContainer.appendChild(checkbox)
10405
10406    // Image
10407    if (property.addonProductImage) {
10408      const imageContainer = document.createElement('div')
10409      imageContainer.classList.add('w-16', 'h-16', 'mr-4')
10410
10411      const image = document.createElement('img')
10412      image.src = property.addonProductImage
10413      image.alt = property.addonProductTitle || property.title
10414      image.classList.add('w-full', 'h-full', 'object-cover', 'rounded')
10415
10416      imageContainer.appendChild(image)
10417      addonContainer.appendChild(imageContainer)
10418    }
10419
10420    // Content (Title, Price, Link)
10421    const contentContainer = document.createElement('div')
10422    contentContainer.classList.add('flex', 'flex-col', 'flex-grow')
10423
10424    const titleElement = document.createElement('span')
10425    titleElement.classList.add('font-bold', 'mb-1')
10426    titleElement.textContent = property.addonProductTitle || property.title
10427
10428    const priceElement = document.createElement('span')
10429    priceElement.classList.add('text-sm', 'text-gray-600')
10430    priceElement.textContent = '+' + (property.addonProductPrice || property.addonPrice)
10431
10432    contentContainer.appendChild(titleElement)
10433    contentContainer.appendChild(priceElement)
10434
10435    if (property.addonProductLink) {
10436      const link = document.createElement('a')
10437      link.href = property.addonProductLink
10438      link.target = '_blank'
10439      link.classList.add('text-blue-600', 'hover:underline', 'text-sm', 'mt-1')
10440      link.textContent = property.addonProductLinkText || 'Read More'
10441      contentContainer.appendChild(link)
10442    }
10443
10444    // Add content container last
10445    addonContainer.appendChild(contentContainer)
10446
10447    return addonContainer
10448  },
10449  renderFile: function (property) {
10450
10451    // Wrapper
10452    const fileContainer = document.createElement('div')
10453    fileContainer.classList.add(...this.config.styling.fileContainerClasses.split(' '))
10454
10455    // Input
10456    const file = document.createElement('input')
10457    file.classList.add(...this.config.styling.fileInputClasses.split(' '))
10458    file.classList.add('qs-cart-product-file')
10459    file.setAttribute('id', 'file-' + property.id)
10460    file.setAttribute('type', 'file')
10461    file.setAttribute('name', 'product_fields_files[' + property.id + ']')
10462    file.setAttribute('accept', this.config.fieldsAttributes.fileInputAccept)
10463    if (property.required === '1') {
10464      file.setAttribute('required', true)
10465      file.setAttribute('data-is-required', true)
10466    }
10467    file.style.display = 'none'
10468
10469    file.setAttribute('data-qb-property', property.id)
10470    fileContainer.append(file)
10471
10472    const button = document.createElement('label')
10473    button.setAttribute('for', 'file-' + property.id)
10474    button.classList.add(...this.config.styling.fileInputButtonClasses.split(' '))
10475    button.innerText = this.data.texts.chooseFile
10476    file.addEventListener('change', function () {
10477      filename.innerText = file.files[0].name
10478    })
10479
10480    fileContainer.append(button)
10481
10482    const filename = document.createElement('span')
10483    filename.innerText = this.data.texts.noFileChosen
10484    filename.classList.add(...this.config.styling.fileInputFilenameClasses.split(' '))
10485    fileContainer.append(filename)
10486
10487    return fileContainer
10488  },
10489  renderLongtext: function (property) {
10490    const longtext = document.createElement('textarea')
10491    longtext.classList.add(
10492      ...this.config.styling.longTextInputClasses.split(' ')
10493    )
10494    longtext.setAttribute('name', property.name)
10495    longtext.setAttribute('maxlength', 250)
10496
10497    return longtext
10498  },
10499  renderShorttext: function (property) {
10500    const shorttext = document.createElement('input')
10501    shorttext.classList.add(
10502      ...this.config.styling.shortTextInputClasses.split(' ')
10503    )
10504    shorttext.setAttribute('type', 'text')
10505    shorttext.setAttribute('name', property.name)
10506    shorttext.setAttribute('maxlength', 250)
10507
10508    return shorttext
10509  },
10510  renderReCaptcha: function () {
10511
10512    /* PRODUCT FIELD FILE UPLOAD START */
10513    if (document.readyState === "complete" || document.readyState === "interactive") {
10514
10515      const propertiesForm = document.querySelector('#ProductPropertiesForm')
10516
10517      if (!propertiesForm) {
10518        console.error('Could not find #ProductPropertiesForm')
10519        return false
10520      }
10521
10522      propertiesForm.dataset.prevent = '1'
10523
10524      var recaptchaDiv = document.createElement('div')
10525      recaptchaDiv.setAttribute('class', 'g-recaptcha')
10526      recaptchaDiv.setAttribute('data-sitekey', recaptchaSiteKey)
10527      recaptchaDiv.setAttribute('data-callback', 'recaptchaSubmit')
10528      recaptchaDiv.setAttribute('data-size', 'invisible')
10529      propertiesForm.prepend(recaptchaDiv)
10530
10531      var url = "https://www.google.com/recaptcha/api.js"
10532      var script = document.createElement('script')
10533      script.src = url
10534      script.type = 'text/javascript'
10535      document.getElementsByTagName('head')[0].appendChild(script)
10536    }
10537  },
10538  uploadFiles: function () {
10539    return new Promise((resolve, reject) => {
10540      const form = document.querySelector('#ProductPropertiesForm')
10541
10542      if (form && form.querySelectorAll('input[type="file"]').length > 0) {
10543        var filesToUpload = new FormData()
10544        Array.from(form.querySelectorAll('input[type="file"]')).forEach(function (tag) {
10545          Array.from(tag.files).forEach(function (file) {
10546            filesToUpload.append(tag.name, file)
10547          })
10548        })
10549
10550        var buy_btn_text = null, wait_btn_text = null
10551        var buybtn_selector = document.querySelector('.qs-cart-submit')
10552
10553        var xhr = new XMLHttpRequest()
10554        xhr.open('POST', '/cart/misc', true)
10555        xhr.setRequestHeader('cache-control', 'no-cache')
10556        xhr.responseType = 'json'
10557
10558        var app = this
10559
10560        xhr.onload = function () {
10561          if (xhr.status >= 200 && xhr.status < 300) {
10562            var data = xhr.response
10563
10564            // Reset button
10565            if (buy_btn_text && buy_btn_text.length !== 0) {
10566              buybtn_selector.value = buy_btn_text
10567              buybtn_selector.innerHTML = buy_btn_text
10568            }
10569            buybtn_selector.removeAttribute('disabled')
10570
10571            // Remove progress bar
10572            if (form.querySelector('.productproperties-file-progress')) {
10573              form.querySelector('.productproperties-file-progress').remove()
10574            }
10575
10576            if (data?.properties) {
10577              if (data.properties.errors) {
10578                alert(data.properties.errors)
10579
10580                return false
10581              } else {
10582                data.properties.forEach(function (property, key) {
10583                  // document.querySelector('.product-fields-file:eq('+property.property_id+')').value = property.value
10584                })
10585
10586                document.querySelector('.qs-cart').dataset.prevent = 0
10587                document.querySelector('.qs-cart').dataset.fileuploadstatus = 1
10588                // document.querySelector('.qs-cart').submit()
10589
10590                resolve(true)
10591              }
10592            } else {
10593
10594              // Handle error
10595              console.error('No properties returned from file upload.')
10596              reject(false)
10597            }
10598          } else {
10599
10600            // Handle error
10601            console.error(xhr.statusText)
10602
10603            reject(false)
10604          }
10605        }
10606
10607        xhr.upload.onloadstart = function (e) {
10608
10609          // Progress bar
10610          if (form.querySelectorAll('.productproperties-file-progress').length == 0) {
10611
10612            // Container
10613            const progressContainer = document.createElement('div')
10614            if (app.config?.styling?.fileUploadProgressContainerClasses) {
10615              progressContainer.classList.add(...app.config.styling.fileUploadProgressContainerClasses.split(' '))
10616            }
10617            progressContainer.classList.add('progress', 'productproperties-file-progress')
10618
10619            // Background
10620            const progressBarBackground = document.createElement('div')
10621            if (app.config?.styling?.fileUploadProgressBarBackgroundClasses) {
10622              progressBarBackground.classList.add(...app.config.styling.fileUploadProgressBarBackgroundClasses.split(' '))
10623            }
10624            progressBarBackground.style.zIndex = 1
10625            progressContainer.appendChild(progressBarBackground)
10626
10627            // Bar
10628            const progressBar = document.createElement('div')
10629            if (app.config?.styling?.fileUploadProgressBarClasses) {
10630              progressBar.classList.add(...app.config.styling.fileUploadProgressBarClasses.split(' '))
10631              progressBar.innerText = "&nbsp;"
10632            }
10633            progressBar.style.zIndex = 2
10634            progressBar.classList.add('progress-bar')
10635            progressContainer.appendChild(progressBar)
10636
10637            form.querySelectorAll('input[type="file"]')[form.querySelectorAll('input[type="file"]').length - 1].parentNode.parentNode.insertAdjacentElement('beforeEnd', progressContainer)
10638          }
10639        }
10640
10641        xhr.upload.onloadend = function (e) { }
10642
10643        xhr.upload.onprogress = function (e) {
10644          var percentComplete = Math.ceil((e.loaded / e.total) * 100)
10645        }
10646
10647        xhr.upload.onprogress = function (evt) {
10648
10649          let progressBar
10650          if (form.querySelectorAll('.productproperties-file-progress').length == 0) {
10651            form.querySelectorAll('input[type="file"]')[0].insertAdjacentHTML('afterend', '<div class="progress productproperties-file-progress ' + app.config.styling.fileUploadProgressContainerClasses + '"><div class="progress-bar ' + app.config.styling.fileUploadProgressBarClasses + '"></div></div>')
10652            progressBar = form.querySelectorAll('.productproperties-file-progress')
10653          }
10654
10655          if (evt.lengthComputable) {
10656            var percentComplete = evt.loaded / evt.total
10657            percentComplete = Math.round(percentComplete * 100)
10658
10659
10660            let progressBarBar = form.querySelectorAll('.productproperties-file-progress')[0].querySelector('.progress-bar')
10661            progressBarBar.style.width = percentComplete + '%'
10662            progressBarBar.innerHTML = percentComplete + '%'
10663          }
10664        }
10665
10666
10667        xhr.onerror = function () {
10668          console.error(xhr.statusText)
10669        }
10670
10671        xhr.send(filesToUpload)
10672
10673        // output = false // set as false when this function is executed as sync.
10674      } else {
10675        return true
10676      }
10677
10678      // console.log('Returns: ' + output)
10679      // return output
10680    })
10681  },
10682
10683})
10684
10685function recaptchaSubmit(token) {
10686  fetch('/apps/productproperties/recaptcha', {
10687    method: 'POST',
10688    body: 'recaptchatoken=' + token,
10689    headers: {
10690      'Content-Type': 'application/x-www-form-urlencoded'
10691    }
10692  })
10693    .then((response) => response.json())
10694    .then((data) => {
10695      if (data.success) {
10696        document.querySelector('#ProductPropertiesForm').dataset.prevent = 0
10697
10698        // Verification successful. Upload the file and add the item to the cart
10699        window.apps.QuickbutikProductProperties.uploadFiles().then(async (uploadSuccess) => {
10700          Alpine.store('cart').addItemToCart(window.bodyFormData)
10701        })
10702
10703        try {
10704          // Reset after submit
10705          grecaptcha.reset()
10706        } catch (e) {
10707          console.log(e)
10708        }
10709
10710        document.querySelector('#ProductPropertiesForm').dataset.prevent = 1
10711
10712        return true
10713      } else {
10714        alert('ReCaptcha error: Please try again.')
10715      }
10716    })
10717}
10718
10719
10720
10721</script>
10721<script type="text/javascript">window.registerApp({
10722  name: 'Ribbons',
10723  identifier: 'QuickbutikRibbons',
10724  description: 'Display ribbons on the top of product items',
10725  version: '2.0.0',
10726  pages: ['list', 'start', 'product'],
10727  data: {},
10728  config: {
10729    productItems: {
10730      productItemSelector: '[data-qb-selector="product-item"]',
10731      appsContainer: '[data-qb-selector="product-item"]',
10732    },
10733    productPage: {
10734      productSelector: '[data-qb-selector="product"]',
10735      appsContainer: '[data-qb-selector="product"]',
10736    },
10737    style: {
10738      // NOTE: legacy client-side fallbacks, superseded by the server-side
10739      // $ribbon_style_defaults in shop/apps.php (the source of truth). These are
10740      // only used as `font_size || fontSize` / `font_weight || fontWeight`, and
10741      // the server now always sends non-empty values, so they're unreachable.
10742      // Don't "fix" the stale `modern.fontWeight: 600` here in isolation &mdash; the
10743      // canonical default is bold; change it in apps.php if you change it at all.
10744      ribbons: {
10745        classic: {
10746          offset: 8,
10747          ribbonHeight: 30,
10748          squareSize: 120,
10749          fontSize: 12,
10750          fontWeight: 700,
10751          textClasses: 'text-white',
10752        },
10753        rectangle: {
10754          borderRadius: 4,
10755          offsetX: -8,
10756          offsetY: 10,
10757          paddingX: 8,
10758          paddingY: 4,
10759          fontSize: 14,
10760          fontWeight: 700,
10761          textClasses: 'text-white',
10762        },
10763        circle: {
10764          offsetX: -8,
10765          offsetY: 10,
10766          paddingX: 20,
10767          paddingY: 10,
10768          fontSize: 14,
10769          fontWeight: 700,
10770          textClasses: 'text-white',
10771        },
10772        modern: {
10773          paddingX: 12,
10774          paddingY: 6,
10775          fontSize: 12,
10776          fontWeight: 600,
10777          textClasses: 'text-white',
10778        },
10779        vertical: {
10780          paddingX: 4,
10781          paddingY: 16,
10782          fontSize: 12,
10783          fontWeight: 700,
10784          textClasses: 'text-white',
10785        },
10786      },
10787    },
10788  },
10789  onMount: function ({ appData, page }) {
10790    console.debug(`${this.identifier}@onMount`)
10791
10792    const bodyFormData = new FormData()
10793    let itemSelector = ''
10794
10795    if (['list', 'start'].includes(page)) {
10796      itemSelector = this.config.productItems.productItemSelector
10797    }
10798    if (page == 'product') {
10799      itemSelector = this.config.productPage.productSelector
10800    }
10801
10802    bodyFormData.set(
10803      'products',
10804      [...document.querySelectorAll(itemSelector)].map(
10805        (product) => product.dataset.productId
10806      )
10807    )
10808    bodyFormData.set('currentPage', document.body.dataset?.qbPage)
10809
10810    fetch(qs_store_url + '/apps/productribbons/fetch', {
10811      method: 'POST',
10812      body: bodyFormData,
10813    })
10814      .then((response) => response.json())
10815      .then((data) => {
10816        if (data?.products) {
10817          const stackingDirection = data.stacking_direction || 'horizontal'
10818
10819          data.products.forEach((productData) => {
10820            let ribbonQuerySelector = ''
10821            if (['list', 'start'].includes(page)) {
10822              ribbonQuerySelector = `${this.config.productItems.appsContainer}[data-product-id="${productData.id}"]`
10823            } else if (page == 'product') {
10824              ribbonQuerySelector = `${this.config.productPage.appsContainer}[data-product-id="${productData.id}"], ${this.config.productItems.appsContainer}[data-product-id="${productData.id}"]`
10825            }
10826
10827            document
10828              .querySelectorAll(ribbonQuerySelector)
10829              .forEach((product) => {
10830                // Separate vertical ribbons from others (they position independently)
10831                const sides = { left: [], right: [] }
10832                const standalone = []
10833                productData.ribbons.forEach((ribbon) => {
10834                  if (ribbon.type === 'vertical') {
10835                    standalone.push(ribbon)
10836                  } else {
10837                    const side = ribbon.position === 'left' ? 'left' : 'right'
10838                    sides[side].push(ribbon)
10839                  }
10840                })
10841
10842                // Create a stacking container for each side that has ribbons
10843                Object.entries(sides).forEach(([side, ribbons]) => {
10844                  if (ribbons.length === 0) return
10845
10846                  const stack = this._createRibbonStack(side, ribbons, stackingDirection)
10847                  if (stack) {
10848                    product.insertAdjacentElement('beforeend', stack)
10849                  }
10850                })
10851
10852                // Render vertical ribbons with their own absolute positioning
10853                standalone.forEach((ribbon) => {
10854                  const el = this.renderRibbon(ribbon)
10855                  if (el) {
10856                    el.style.position = 'absolute'
10857                    el.style.top = '0'
10858                    el.style[ribbon.position === 'left' ? 'left' : 'right'] = '0'
10859                    el.style.pointerEvents = 'none'
10860                    product.insertAdjacentElement('beforeend', el)
10861                  }
10862                })
10863              })
10864          })
10865        }
10866      })
10867      .catch((error) => {
10868        console.error('Error fetching product ribbons:', error)
10869      })
10870  },
10871  _createRibbonStack: function (side, ribbons, stackingDirection) {
10872    const flexDirection = stackingDirection === 'horizontal' ? 'row' : 'column'
10873    const stack = document.createElement('div')
10874    stack.setAttribute('data-qb-ribbon-stack', side)
10875    stack.style.position = 'absolute'
10876    stack.style.top = '0'
10877    stack.style[side] = '0'
10878    stack.style.display = 'flex'
10879    stack.style.flexDirection = flexDirection
10880    stack.style.alignItems = side === 'left' ? 'flex-start' : 'flex-end'
10881    stack.style.gap = '6px'
10882    stack.style.pointerEvents = 'none'
10883
10884    ribbons.forEach((ribbon, index) => {
10885      const el = this.renderRibbon({
10886        text: ribbon.text,
10887        bg_start_color: ribbon.bg_start_color,
10888        bg_end_color: ribbon.bg_end_color,
10889        text_color: ribbon.text_color,
10890        position: ribbon.position,
10891        type: ribbon.type,
10892        font_size: ribbon.font_size,
10893        font_weight: ribbon.font_weight,
10894        custom_css: ribbon.custom_css,
10895      })
10896
10897      if (el) {
10898        // Convert from absolute to relative for flex stacking
10899        el.classList.remove('absolute')
10900        el.style.position = 'relative'
10901
10902        // First ribbon: preserve original offsets as margin for backward compatibility
10903        if (index === 0) {
10904          if (el.style.top && el.style.top !== '0px') {
10905            el.style.marginTop = el.style.top
10906          }
10907          const sideValue = side === 'left' ? el.style.left : el.style.right
10908          if (sideValue && sideValue !== '0px' && sideValue !== '0') {
10909            el.style[side === 'left' ? 'marginLeft' : 'marginRight'] = sideValue
10910          }
10911        }
10912
10913        el.style.top = ''
10914        el.style.left = ''
10915        el.style.right = ''
10916
10917        stack.appendChild(el)
10918      } else {
10919        console.warn(`Failed to render ribbon with type: ${ribbon.type}`)
10920      }
10921    })
10922
10923    return stack
10924  },
10925  renderRibbon: function ({
10926    bg_end_color,
10927    bg_start_color,
10928    text_color,
10929    position,
10930    text,
10931    type,
10932    font_size,
10933    font_weight,
10934    custom_css,
10935  }) {
10936
10937    if (type === 'classic') {
10938      const { offset, squareSize, ribbonHeight, fontSize, fontWeight, textClasses } =
10939        this.config.style.ribbons.classic
10940
10941      const container = document.createElement('div')
10942      container.setAttribute('data-qb-ribbon', '')
10943      container.classList.add('absolute', 'overflow-hidden')
10944      container.style.pointerEvents = 'none'
10945      container.style.display = 'block'
10946      container.style.top = `-${offset}px`
10947      container.style[
10948        `${position === 'left' ? 'left' : 'right'}`
10949      ] = `-${offset}px`
10950      container.style.height = `${squareSize}px`
10951      container.style.width = `${squareSize}px`
10952      if (position === 'left') {
10953        container.style.transform = 'rotate(270deg)'
10954      }
10955
10956      const tinySquare1 = document.createElement('div')
10957      tinySquare1.classList.add('absolute', 'top-0', 'left-0')
10958      tinySquare1.style.backgroundColor = bg_start_color
10959      tinySquare1.style.display = 'block'
10960      tinySquare1.style.height = `${offset}px`
10961      tinySquare1.style.width = `${offset}px`
10962
10963      const tinySquare2 = document.createElement('div')
10964      tinySquare2.classList.add('absolute', 'bottom-0', 'right-0')
10965      tinySquare2.style.pointerEvents = 'none'
10966      tinySquare2.style.backgroundColor = bg_end_color
10967      tinySquare2.style.display = 'block'
10968      tinySquare2.style.height = `${offset}px`
10969      tinySquare2.style.width = `${offset}px`
10970
10971      const ribbon = document.createElement('div')
10972      ribbon.classList.add('absolute', 'flex', 'items-center', 'justify-center')
10973
10974      if (textClasses) {
10975        textClasses.split(' ').forEach((cls) => {
10976          ribbon.classList.add(cls)
10977        })
10978      }
10979
10980      ribbon.style.pointerEvents = 'none'
10981      ribbon.style.height = `${ribbonHeight}px`
10982      ribbon.style.width = `${squareSize}px`
10983      ribbon.style.transformOrigin = 'bottom left'
10984      ribbon.style.top = `-${ribbonHeight}px`
10985      ribbon.style.left = '0'
10986      ribbon.style.transform = 'rotate(45deg)'
10987      ribbon.style.width = `${Math.ceil(squareSize * Math.sqrt(2))}px`
10988      ribbon.style.background = `linear-gradient(${bg_start_color} 0%, ${bg_end_color} 100%)`
10989      ribbon.style.boxShadow = '0 3px 10px -5px rgba(0, 0, 0, 1)'
10990      ribbon.style.color = text_color
10991      ribbon.style.fontSize = `${font_size || fontSize}px`
10992      ribbon.style.fontWeight = font_weight || fontWeight
10993
10994      const textNode = document.createTextNode(text)
10995      ribbon.appendChild(textNode)
10996
10997      container.appendChild(tinySquare1)
10998      container.appendChild(tinySquare2)
10999      container.appendChild(ribbon)
11000
11001      if (custom_css) {
11002        custom_css.split(' ').forEach((cls) => {
11003          if (cls) container.classList.add(cls)
11004        })
11005      }
11006
11007      return container
11008    }
11009
11010    if (type === 'rectangle') {
11011      const {
11012        borderRadius,
11013        offsetX,
11014        offsetY,
11015        paddingX,
11016        paddingY,
11017        fontSize,
11018        fontWeight,
11019        textClasses,
11020      } = this.config.style.ribbons.rectangle
11021
11022      const container = document.createElement('div')
11023      container.setAttribute('data-qb-ribbon', '')
11024      container.classList.add(
11025        'absolute',
11026        'flex',
11027        'justify-center',
11028        'items-center'
11029      )
11030      container.style.pointerEvents = 'none'
11031      container.style.top = `${offsetY}px`
11032      container.style[position === 'left' ? 'left' : 'right'] = `${offsetX}px`
11033      container.style.padding = `${paddingY}px ${paddingX}px`
11034      container.style.background = `linear-gradient(${bg_start_color} 0%, ${bg_end_color} 100%)`
11035      container.style.borderRadius = `${borderRadius}px`
11036
11037      const textNode = document.createTextNode(text)
11038
11039      const span = document.createElement('span')
11040      span.appendChild(textNode)
11041
11042      if (textClasses) {
11043        textClasses.split(' ').forEach((cls) => {
11044          span.classList.add(cls)
11045        })
11046      }
11047
11048      span.style.color = text_color
11049      span.style.fontSize = `${font_size || fontSize}px`
11050      span.style.fontWeight = font_weight || fontWeight
11051      container.appendChild(span)
11052
11053      if (custom_css) {
11054        custom_css.split(' ').forEach((cls) => {
11055          if (cls) container.classList.add(cls)
11056        })
11057      }
11058
11059      return container
11060    }
11061
11062    if (type === 'circle') {
11063      const { offsetX, offsetY, paddingX, paddingY, fontSize, fontWeight, textClasses } =
11064        this.config.style.ribbons.circle
11065
11066      const container = document.createElement('div')
11067      container.setAttribute('data-qb-ribbon', '')
11068      container.classList.add('absolute')
11069      container.style.pointerEvents = 'none'
11070      container.style.display = 'block'
11071      container.style.top = `${offsetY}px`
11072      container.style[position === 'left' ? 'left' : 'right'] = `${offsetX}px`
11073      container.style.padding = `${paddingY}px ${paddingX}px`
11074      container.style.borderRadius = '50%'
11075      container.style.background = `linear-gradient(${bg_start_color} 0%, ${bg_end_color} 100%)`
11076
11077      const textNode = document.createTextNode(text)
11078
11079      const span = document.createElement('span')
11080      span.appendChild(textNode)
11081
11082      if (textClasses) {
11083        textClasses.split(' ').forEach((cls) => {
11084          span.classList.add(cls)
11085        })
11086      }
11087
11088      span.style.color = text_color
11089      span.style.fontSize = `${font_size || fontSize}px`
11090      span.style.fontWeight = font_weight || fontWeight
11091      container.appendChild(span)
11092
11093      if (custom_css) {
11094        custom_css.split(' ').forEach((cls) => {
11095          if (cls) container.classList.add(cls)
11096        })
11097      }
11098
11099      return container
11100    }
11101
11102    if (type === 'modern') {
11103      const {
11104        paddingX,
11105        paddingY,
11106        fontSize,
11107        fontWeight,
11108        textClasses,
11109      } = this.config.style.ribbons.modern
11110
11111      const container = document.createElement('div')
11112      container.setAttribute('data-qb-ribbon', '')
11113      container.classList.add(
11114        'absolute',
11115        'flex',
11116        'justify-center',
11117        'items-center'
11118      )
11119      container.style.pointerEvents = 'none'
11120      container.style.top = '0px'
11121      container.style[position === 'left' ? 'left' : 'right'] = '0px'
11122      container.style.padding = `${paddingY}px ${paddingX}px`
11123      container.style.backgroundColor = bg_start_color
11124      container.style.backdropFilter = 'blur(4px)'
11125
11126      const textNode = document.createTextNode(text)
11127
11128      const span = document.createElement('span')
11129      span.appendChild(textNode)
11130
11131      if (textClasses) {
11132        textClasses.split(' ').forEach((cls) => {
11133          span.classList.add(cls)
11134        })
11135      }
11136
11137      span.style.color = text_color
11138      span.style.fontSize = `${font_size || fontSize}px`
11139      span.style.fontWeight = font_weight || fontWeight
11140      span.style.letterSpacing = '0.025em'
11141      container.appendChild(span)
11142
11143      if (custom_css) {
11144        custom_css.split(' ').forEach((cls) => {
11145          if (cls) container.classList.add(cls)
11146        })
11147      }
11148
11149      return container
11150    }
11151
11152    if (type === 'vertical') {
11153      const {
11154        paddingX,
11155        paddingY,
11156        fontSize,
11157        fontWeight,
11158        textClasses,
11159      } = this.config.style.ribbons.vertical
11160
11161      const container = document.createElement('div')
11162      container.setAttribute('data-qb-ribbon', '')
11163      container.classList.add(
11164        'absolute',
11165        'flex',
11166        'justify-center',
11167        'items-center'
11168      )
11169      container.style.pointerEvents = 'none'
11170      container.style.top = '0px'
11171      container.style[position === 'left' ? 'left' : 'right'] = '0px'
11172      container.style.padding = `${paddingY}px ${paddingX}px`
11173      container.style.minHeight = '80px'
11174      container.style.backgroundColor = bg_start_color
11175      container.style.writingMode = 'vertical-lr'
11176      container.style.textOrientation = 'mixed'
11177
11178      const textNode = document.createTextNode(text)
11179
11180      const span = document.createElement('span')
11181      span.appendChild(textNode)
11182
11183      if (textClasses) {
11184        textClasses.split(' ').forEach((cls) => {
11185          span.classList.add(cls)
11186        })
11187      }
11188
11189      span.style.color = text_color
11190      span.style.fontSize = `${font_size || fontSize}px`
11191      span.style.fontWeight = font_weight || fontWeight
11192      container.appendChild(span)
11193
11194      if (custom_css) {
11195        custom_css.split(' ').forEach((cls) => {
11196          if (cls) container.classList.add(cls)
11197        })
11198      }
11199
11200      return container
11201    }
11202
11203    // Fallback for unknown ribbon types
11204    console.warn(`Unknown ribbon type: ${type}. Expected: classic, rectangle, circle, modern, or vertical`)
11205    return null
11206  },
11207})
11208</script>
11208<script type="text/javascript">(function () {
11209  window.registerApp({
11210    name: "LanguageModal",
11211    identifier: "QuickbutikLanguageModal",
11212    description: "Language Modal",
11213    version: "1.0.0",
11214    pages: ["list", "start", "product"],
11215    data: {},
11216    config: {},
11217    onMount: function ({ appData, page }) {
11218      this.loadDependencies().then(function () {
11219        new LanguageModal(appData).check();
11220      });
11221    },
11222    loadDependencies: function () {
11223      return Promise.all([
11224        loadScript("/assets/shopassets/modal/modal.js"),
11225        loadStylesheet("/assets/shopassets/modal/modal.css"),
11226
11227        loadScript("/assets/shopassets/language-modal/language-modal-v4.js"),
11228      ])
11229    }
11230  });
11231
11232  function loadScript(src) {
11233    if (document.querySelector(`script[src="${src}"]`)) {
11234      return Promise.resolve();
11235    }
11236
11237    return new Promise(function (resolve, reject) {
11238      const script = document.createElement("script");
11239      script.src = src;
11240      script.async = true;
11241      script.onload = resolve;
11242      script.onerror = reject;
11243      document.head.append(script);
11244    });
11245  }
11246
11247  function loadStylesheet(href) {
11248    if (document.querySelector(`link[href="${href}"]`)) {
11249      return Promise.resolve();
11250    }
11251
11252    return new Promise(function (resolve, reject) {
11253      const link = document.createElement("link");
11254      link.href = href;
11255      link.rel = "stylesheet";
11256      link.onload = resolve;
11257      link.onerror = reject;
11258      document.head.append(link);
11259    });
11260  }
11261})();
11262
11263</script>
11263<script type="text/javascript">window.registerApp({
11264  name: "ProductReviews",
11265  identifier: "QuickbutikProductReviews",
11266  description: "Show product reviews",
11267  version: "1.3.0",
11268  pages: ["product", "list", "start"],
11269  data: {},
11270  config: {
11271    containerSelector: "#productReviews",
11272    listItemContainerSelector:
11273      '[data-qb-selector="product-apps-reviews-item-reviews"]',
11274    themeBasedReviews: true,
11275    appContainerSelector: '[data-qb-selector="product-apps-reviews"]',
11276    appContainerFormSelector: '[data-qb-selector="product-apps-reviews-form"]',
11277    appContainerStarsSelector:
11278      '[data-qb-selector="product-apps-reviews-total"]',
11279    appContainerButtonSelector:
11280      '[data-qb-selector="product-apps-reviews-write-review"]',
11281    appContainerReviewsSelector:
11282      '[data-qb-selector="product-apps-reviews-items"]',
11283    star: {
11284      path: "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z",
11285      color: {
11286        active: "#F59E0B",
11287        inactive: "#E5E7EB",
11288      },
11289    },
11290    stars: {
11291      size: "w-5 h-5",
11292      productPageSize: "w-5 h-5",
11293    },
11294    styling: {
11295      articleWidth: 85,
11296      classes: {
11297        headerContainer: "flex items-center justify-between py-6 border-b",
11298        addButton:
11299          "inline-flex items-center justify-center w-12 h-12 border hover:border-gray-500",
11300        headerRatingContainer: "flex items-center gap-3",
11301        headerText: "text-xl font-bold font-heading",
11302        ratingCount: "ml-2",
11303        reviewsContainer: "mt-4",
11304        reviewsArticleContainer: "flex gap-4 overflow-hidden",
11305        article: "pb-12 border-b",
11306        articleNameContainer: "flex items-center mb-4 space-x-4",
11307        articleName: "space-y-2 font-medium",
11308        articleRatingContainer: "flex items-center mb-2",
11309        articleContent: "mb-2 text-gray-500 dark:text-gray-400",
11310        articleShowMoreButton:
11311          "text-xs text-xs text-gray-500 ml-2 cursor-pointer",
11312        replyContainer: "border mt-3 ml-3 p-3",
11313        replyText: "text-xs text-gray-500 dark:text-gray-400",
11314        starsContainer: "flex flex-col gap-4",
11315        form: "my-4 flex flex-col gap-6",
11316        formRatingDiv: "flex flex-col gap-4",
11317        formRatingLabel: "text-sm font-bold text-gray-700",
11318        formGroup: "flex flex-col gap-4",
11319        textInput:
11320          "block w-full p-3 text-xs text-black placeholder-gray-500 transition-all bg-white border border-gray-200 focus:border-gray-700 focus:outline-none",
11321        formSubmitButton:
11322          "block w-full cursor-pointer bg-primary text-center text-white font-bold py-5 px-8 uppercase transition duration-200 hover:opacity-70",
11323        formErrorMessageContainer: "text-red-500 text-sm font-bold",
11324        formThankYouMessageContainer: "flex flex-col gap-4 my-12",
11325        formThankYouMessageLabel: "text-sm font-bold text-gray-700",
11326      },
11327      gradientOverlay: `linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1) 80%)`,
11328      articleFade: {
11329        background:
11330          "linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1))",
11331        classes: "h-12",
11332      },
11333      articleMaxHeight: "100px",
11334    },
11335  },
11336
11337  onMount: function ({ appData, page }) {
11338    console.debug(`${this.identifier}@onMount`);
11339
11340    const bodyFormData = new FormData();
11341    let itemSelector = "";
11342
11343    // Respect backend flag: show_in_lists
11344    const normalizeShowInLists = (val) => {
11345      if (typeof val === "boolean") return val;
11346      if (val === 1 || val === "1" || val === "true") return true;
11347      if (val === 0 || val === "0" || val === "false") return false;
11348      return true; // default to true if missing
11349    };
11350    this.data.showInLists = normalizeShowInLists(appData?.productreviews?.show_in_lists);
11351    
11352    // Store review settings
11353    this.data.reviewSectionDescription = appData?.productreviews?.review_section_description || null;
11354    this.data.reviewTextPlaceholder = appData?.productreviews?.review_text_placeholder || null;
11355    
11356    // Set star color from settings (backend resolves "primary" to actual color)
11357    if (appData?.productreviews?.star_color) {
11358      this.config.star.color.active = appData.productreviews.star_color;
11359      this.injectStarColorStyles(appData.productreviews.star_color);
11360    }
11361
11362    if (["list", "start"].includes(page)) {
11363      itemSelector = '[data-qb-selector="product-item"]';
11364    } else if (page === "product") {
11365      // Main product + related product cards
11366      itemSelector = '[data-qb-selector="product"], [data-qb-selector="product-item"]';
11367    }
11368    // If needed, we could scope to only main product:
11369    // if (page === "product") { itemSelector = '[data-qb-selector="product"]'; }
11370
11371    // On product page, render main product UI from top-level meta
11372    if (page === "product") {
11373      const mainProductEl = document.querySelector('[data-qb-selector="product"]');
11374      const mainMeta = appData?.productreviews || {};
11375      const mainProductId = mainProductEl?.getAttribute("data-product-id");
11376      if (mainProductEl && mainProductId) {
11377        const mainProductData = {
11378          id: mainProductId,
11379          rating: mainMeta.rating,
11380          rating_decimal: mainMeta.rating_decimal,
11381          amountTotal: mainMeta.amountTotal,
11382          reviews: mainMeta.reviews,
11383          has_rated: mainMeta.has_rated,
11384        };
11385        this.renderProductReviews(mainProductData, page, mainProductEl);
11386      }
11387    }
11388
11389    if ((page === "list" || page === "start") && !this.data.showInLists) {
11390      return;
11391    }
11392    const existingProducts = appData?.productreviews?.products;
11393    if (Array.isArray(existingProducts) && existingProducts.length > 0) {
11394      if (page === "product" && !this.data.showInLists) {
11395        // Do not render related items when disabled
11396        return;
11397      }
11398      existingProducts.forEach((productData) => {
11399        let productQuerySelector = "";
11400        if (["list", "start"].includes(page)) {
11401          productQuerySelector = `[data-qb-selector="product-item"][data-product-id="${productData.id}"]`;
11402        } else if (page === "product") {
11403          // Only related product cards; main product handled from top-level meta above
11404          productQuerySelector = `[data-qb-selector="product-item"][data-product-id="${productData.id}"]`;
11405        }
11406
11407        document
11408          .querySelectorAll(productQuerySelector)
11409          .forEach((product) => {
11410            this.renderProductReviews(productData, page, product);
11411          });
11412      });
11413      return;
11414    }
11415
11416    // Get all product IDs on the page (same as productribbons)
11417    const productElements = document.querySelectorAll(itemSelector);
11418    if (productElements.length === 0) {
11419      return;
11420    }
11421
11422    let productIds = [...productElements]
11423      .map((product) => product.dataset.productId)
11424      .filter((id) => id && id !== "0");
11425
11426    // On product page, exclude main product ID (we render it from top-level meta)
11427    if (page === "product") {
11428      const mainProductId = document.querySelector('[data-qb-selector="product"]')?.getAttribute('data-product-id');
11429      if (mainProductId) {
11430        productIds = productIds.filter((id) => id !== mainProductId);
11431      }
11432    }
11433
11434    if (page === 'product' && !this.data.showInLists) {
11435      return;
11436    }
11437
11438    if (productIds.length === 0) {
11439      return;
11440    }
11441
11442    bodyFormData.set("products", productIds.join(","));
11443    bodyFormData.set("currentPage", document.body.dataset?.qbPage);
11444
11445    // Fetch reviews data from endpoint (same pattern as productribbons)
11446    fetch(qs_store_url + "/apps/productreviews/fetch", {
11447      method: "POST",
11448      body: bodyFormData,
11449    })
11450      .then((response) => response.json())
11451      .then((data) => {
11452        console.debug(
11453          `${this.identifier}: Received data for ${
11454            data?.products?.length || 0
11455          } products`
11456        );
11457
11458        // Store settings if fetched
11459        if (data?.review_section_description !== undefined) {
11460          this.data.reviewSectionDescription = data.review_section_description;
11461        }
11462        if (data?.review_text_placeholder !== undefined) {
11463          this.data.reviewTextPlaceholder = data.review_text_placeholder;
11464        }
11465        
11466        // Set star color from fetched settings (backend resolves "primary" to actual color)
11467        if (data?.star_color) {
11468          this.config.star.color.active = data.star_color;
11469          this.injectStarColorStyles(data.star_color);
11470        }
11471
11472        if (data?.products) {
11473          data?.products.forEach((productData) => {
11474            let productQuerySelector = "";
11475            if (["list", "start", ].includes(page)) {
11476              productQuerySelector = `[data-qb-selector="product-item"][data-product-id="${productData.id}"]`;
11477            } else if (page === "product") {
11478              // Only related product cards; main product handled from top-level meta
11479              productQuerySelector = `[data-qb-selector="product-item"][data-product-id="${productData.id}"]`;
11480            }
11481
11482            document
11483              .querySelectorAll(productQuerySelector)
11484              .forEach((product) => {
11485                this.renderProductReviews(productData, page, product);
11486              });
11487          });
11488        }
11489      })
11490      .catch((error) => {
11491        console.error(
11492          `${this.identifier}: Error fetching product reviews:`,
11493          error
11494        );
11495      });
11496  },
11497
11498  injectStarColorStyles: function (starColor) {
11499    // Check if style element already exists
11500    const existingStyle = document.getElementById('qb-review-star-styles');
11501    if (existingStyle) {
11502      // Update existing style
11503      existingStyle.textContent = `.qb-review-star-active { color: ${starColor} !important; }`;
11504    } else {
11505      // Create new style element
11506      const styleElement = document.createElement('style');
11507      styleElement.id = 'qb-review-star-styles';
11508      styleElement.textContent = `.qb-review-star-active { color: ${starColor} !important; }`;
11509      document.head.appendChild(styleElement);
11510    }
11511  },
11512
11513  renderProductReviews: function (productData, page, product) {
11514    const { rating } = productData;
11515
11516    if (this.config.themeBasedReviews) {
11517      // Only render full UI for the main product container on product pages
11518      if (page === 'product' && product?.matches?.('[data-qb-selector="product"]')) {
11519        this.renderThemeBasedProductReviews(productData, page);
11520      }
11521    } else {
11522      // Traditional mode: only render full UI for the main product container
11523      if (page === 'product' && product?.matches?.('[data-qb-selector="product"]')) {
11524        this.renderTraditionalProductReviews(productData, page);
11525      }
11526    }
11527
11528    // Always render product item ratings for list/start pages
11529    // On product page, render ratings for related product cards as well
11530    if ((["list", "start"].includes(page) || page === 'product') && rating > 0 && product?.matches?.('[data-qb-selector="product-item"]')) {
11531      this.renderProductItemRating(productData, product, page);
11532    }
11533  },
11534
11535  renderThemeBasedProductReviews: function (productData, page) {
11536    const { rating, rating_decimal, reviews, amountTotal } = productData;
11537
11538    // For product page, render full review interface
11539    if (page === "product") {
11540      // Total review score/stars (if any, in multiple places)
11541      if (rating > 0) {
11542        const containers = document.querySelectorAll(
11543          this.config.appContainerStarsSelector
11544        );
11545        if (containers) {
11546          containers.forEach((container) => {
11547            // Only add if not already populated
11548            if (container.children.length === 0) {
11549              container.appendChild(
11550                this.renderRating(parseFloat(rating_decimal ?? rating), page)
11551              );
11552              const reviewsCount = document.createElement("div");
11553              reviewsCount.classList.add(
11554                ...this.config.styling.classes.ratingCount.split(" ")
11555              );
11556              reviewsCount.textContent = "(" + (amountTotal || 0) + ")";
11557              container.appendChild(reviewsCount);
11558            }
11559          });
11560        }
11561      }
11562
11563      // Write review form
11564      const container = document.querySelector(
11565        this.config.appContainerFormSelector
11566      );
11567      if (container && container.children.length === 0) {
11568        // Extract language tags
11569        const language = container.getAttribute("data-language")
11570          ? JSON.parse(container.getAttribute("data-language"))
11571          : {};
11572
11573        container.appendChild(this.renderForm(language));
11574      }
11575
11576      // Reviews
11577      if (reviews && reviews.length > 0) {
11578        // Reviews container
11579        const reviewsContainer = document.querySelector(
11580          this.config.appContainerReviewsSelector
11581        );
11582        if (reviewsContainer) {
11583          // Add reviews to x-data
11584          reviewsContainer.setAttribute(
11585            "x-init",
11586            "reviews = " + JSON.stringify(reviews)
11587          );
11588        }
11589      }
11590    }
11591  },
11592
11593  renderTraditionalProductReviews: function (productData, page) {
11594    // Only render for product page in traditional mode
11595    if (page === "product") {
11596      const selector = this.config.containerSelector;
11597      const container = document.querySelector(selector);
11598
11599      if (container && container.children.length === 0) {
11600        container.appendChild(this.renderProductReviewsUI(productData, page));
11601      }
11602    }
11603  },
11604
11605  renderProductReviewsUI: function (productData, page) {
11606    const { rating, rating_decimal, has_rated, reviews } = productData;
11607
11608    const mainContainer = document.createElement("div");
11609    mainContainer.setAttribute("x-data", JSON.stringify({ showForm: false }));
11610
11611    const headerContainer = document.createElement("div");
11612    headerContainer.classList.add(
11613      ...this.config.styling.classes.headerContainer.split(" ")
11614    );
11615
11616    const headerRatingContainer = document.createElement("div");
11617    headerRatingContainer.classList.add(
11618      ...this.config.styling.classes.headerRatingContainer.split(" ")
11619    );
11620
11621    const h4 = document.createElement("h4");
11622    h4.textContent = "Reviews";
11623    h4.classList.add(...this.config.styling.classes.headerText.split(" "));
11624
11625    headerRatingContainer.appendChild(h4);
11626
11627    if (rating > 0) {
11628      headerRatingContainer.appendChild(
11629        this.renderRating(parseFloat(rating_decimal ?? rating), page)
11630      );
11631    }
11632
11633    const showRatingFormButton = document.createElement("a");
11634    showRatingFormButton.classList.add(
11635      ...this.config.styling.classes.addButton.split(" ")
11636    );
11637    showRatingFormButton.setAttribute("x-on:click", "showForm = !showForm");
11638    showRatingFormButton.id = "productReviewShowFormButton";
11639
11640    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
11641    svg.setAttribute("width", "12");
11642    svg.setAttribute("height", "12");
11643    svg.setAttribute("viewBox", "0 0 12 12");
11644    svg.setAttribute("fill", "none");
11645
11646    const rect1 = document.createElementNS(
11647      "http://www.w3.org/2000/svg",
11648      "rect"
11649    );
11650    rect1.setAttribute("x", "5");
11651    rect1.setAttribute("width", "2");
11652    rect1.setAttribute("height", "12");
11653    rect1.setAttribute("fill", "#161616");
11654    svg.appendChild(rect1);
11655
11656    const rect2 = document.createElementNS(
11657      "http://www.w3.org/2000/svg",
11658      "rect"
11659    );
11660    rect2.setAttribute("x", "12");
11661    rect2.setAttribute("y", "5");
11662    rect2.setAttribute("width", "2");
11663    rect2.setAttribute("height", "12");
11664    rect2.setAttribute("transform", "rotate(90 12 5)");
11665    rect2.setAttribute("fill", "#161616");
11666    svg.appendChild(rect2);
11667
11668    showRatingFormButton.appendChild(svg);
11669
11670    headerContainer.appendChild(headerRatingContainer);
11671
11672    if (!has_rated) {
11673      headerContainer.appendChild(showRatingFormButton);
11674    }
11675
11676    mainContainer.appendChild(headerContainer);
11677    mainContainer.appendChild(this.renderForm());
11678
11679    if (reviews && reviews.length > 0) {
11680      const reviewsContainer = document.createElement("div");
11681      reviewsContainer.className = "relative";
11682      reviewsContainer.classList.add(
11683        ...this.config.styling.classes.reviewsContainer.split(" ")
11684      );
11685
11686      const reviewsArticleContainer = document.createElement("div");
11687      reviewsArticleContainer.className =
11688        this.config.styling.classes.reviewsArticleContainer;
11689      reviewsArticleContainer.style.overflowX = "auto";
11690      reviewsArticleContainer.style.scrollSnapType = "x mandatory";
11691
11692      reviews.forEach((review) => {
11693        reviewsArticleContainer.appendChild(this.renderReview(review));
11694      });
11695
11696      reviewsContainer.appendChild(reviewsArticleContainer);
11697
11698      if (reviews.length > 1) {
11699        reviewsArticleContainer.lastElementChild.style.marginRight = `${
11700          100 - this.config.styling.articleWidth
11701        }%`;
11702
11703        const gradientOverlay = document.createElement("div");
11704        gradientOverlay.style.position = "absolute";
11705        gradientOverlay.style.top = "0";
11706        gradientOverlay.style.bottom = "0";
11707        gradientOverlay.style.right = "0";
11708        gradientOverlay.style.width = `${
11709          100 - this.config.styling.articleWidth
11710        }%`;
11711        gradientOverlay.style.pointerEvents = "none";
11712        gradientOverlay.style.backgroundImage =
11713          this.config.styling.gradientOverlay;
11714
11715        reviewsContainer.appendChild(gradientOverlay);
11716      }
11717
11718      mainContainer.appendChild(reviewsContainer);
11719    }
11720
11721    return mainContainer;
11722  },
11723
11724  renderReview: function ({ content, name, rating, reply }) {
11725    const article = document.createElement("article");
11726    article.classList.add(...this.config.styling.classes.article.split(" "));
11727    article.style.scrollSnapAlign = "start";
11728    article.style.flex = "0 0 auto";
11729    article.style.width = `${
11730      this.data.reviews?.length > 1 ? this.config.styling.articleWidth : 100
11731    }%`;
11732
11733    const articleNameContainer = document.createElement("div");
11734    articleNameContainer.classList.add(
11735      ...this.config.styling.classes.articleNameContainer.split(" ")
11736    );
11737
11738    const articleName = document.createElement("div");
11739    articleName.classList.add(
11740      ...this.config.styling.classes.articleName.split(" ")
11741    );
11742    const p1 = document.createElement("p");
11743    p1.textContent = name;
11744    articleName.appendChild(p1);
11745    articleNameContainer.appendChild(articleName);
11746    article.appendChild(articleNameContainer);
11747
11748    const div3 = document.createElement("div");
11749    div3.classList.add(
11750      ...this.config.styling.classes.articleRatingContainer.split(" ")
11751    );
11752    div3.appendChild(this.renderRating(rating, "product"));
11753
11754    article.appendChild(div3);
11755
11756    const contentContainer = document.createElement("div");
11757    contentContainer.setAttribute(
11758      "x-data",
11759      `{
11760  showMore: false,
11761  isOverflowing(element) {
11762    return element.scrollHeight > element.clientHeight;
11763  }
11764}`
11765    );
11766
11767    const p2 = document.createElement("p");
11768    p2.className = "relative";
11769    p2.classList.add(...this.config.styling.classes.articleContent.split(" "));
11770
11771    p2.setAttribute("x-bind:class", '{ "overflow-hidden": !showMore }');
11772    p2.setAttribute(
11773      "x-bind:style",
11774      `{ maxHeight: showMore ? "none" : "${this.config.styling.articleMaxHeight}" }`
11775    );
11776
11777    p2.innerHTML = content;
11778
11779    const showMoreLink = document.createElement("a");
11780    showMoreLink.classList.add(
11781      ...this.config.styling.classes.articleShowMoreButton.split(" ")
11782    );
11783
11784    showMoreLink.setAttribute("x-text", 'showMore ? "Show less" : "Show more"');
11785    showMoreLink.setAttribute("x-show", "isOverflowing($refs.content)");
11786    showMoreLink.setAttribute("x-on:click", "showMore = !showMore");
11787    showMoreLink.setAttribute("x-ref", "showMoreLink");
11788
11789    const fadeOverlay = document.createElement("div");
11790    fadeOverlay.className =
11791      "absolute bottom-0 left-0 right-0  pointer-events-none";
11792    fadeOverlay.classList.add(
11793      ...this.config.styling.articleFade.classes.split(" ")
11794    );
11795    fadeOverlay.style.backgroundImage =
11796      this.config.styling.articleFade.background;
11797
11798    fadeOverlay.setAttribute(
11799      "x-show",
11800      "!showMore && isOverflowing($refs.content)"
11801    );
11802
11803    p2.appendChild(fadeOverlay);
11804
11805    contentContainer.appendChild(p2);
11806    p2.setAttribute("x-ref", "content");
11807    contentContainer.appendChild(showMoreLink);
11808    article.appendChild(contentContainer);
11809
11810    if (reply) {
11811      const aside = document.createElement("aside");
11812      aside.classList.add(
11813        ...this.config.styling.classes.replyContainer.split(" ")
11814      );
11815
11816      const p3 = document.createElement("p");
11817
11818      p3.classList.add(...this.config.styling.classes.replyText.split(" "));
11819      p3.textContent = reply;
11820      aside.appendChild(p3);
11821
11822      article.appendChild(aside);
11823    }
11824
11825    return article;
11826  },
11827  renderProductItemRating: function (productData, productItem, page) {
11828    const { id, rating, rating_decimal, amountTotal } = productData;
11829
11830    try {
11831      if (rating && parseFloat(rating) > 0) {
11832        const productItemRatingContainer = document.createElement("div");
11833
11834        const containerClass =
11835          this.config.styling.classes.productItemRatingContainer;
11836        if (containerClass && containerClass.trim() !== "") {
11837          productItemRatingContainer.classList.add(
11838            ...containerClass.split(" ")
11839          );
11840        }
11841
11842        // Create rating with review count
11843        const ratingElement = this.renderRating(
11844          parseFloat(rating_decimal ?? rating),
11845          page
11846        );
11847
11848        // Add review count in parentheses if available
11849        if (amountTotal && parseInt(amountTotal) > 0) {
11850          const reviewCountSpan = document.createElement("span");
11851          reviewCountSpan.classList.add(
11852            "review-count",
11853            "text-sm",
11854            "text-gray-600",
11855            "ml-2"
11856          );
11857          reviewCountSpan.textContent = `(${amountTotal})`;
11858
11859          const ratingWrapper = document.createElement("div");
11860          ratingWrapper.classList.add("flex", "items-center");
11861          ratingWrapper.appendChild(ratingElement);
11862          ratingWrapper.appendChild(reviewCountSpan);
11863
11864          productItemRatingContainer.appendChild(ratingWrapper);
11865        } else {
11866          productItemRatingContainer.appendChild(ratingElement);
11867        }
11868
11869        // Check if listItemContainerSelector is defined and not blank
11870        let targetContainer = productItem;
11871        if (
11872          this.config.listItemContainerSelector &&
11873          this.config.listItemContainerSelector.trim() !== ""
11874        ) {
11875          const listItemContainer = productItem.querySelector(
11876            this.config.listItemContainerSelector
11877          );
11878          if (listItemContainer) {
11879            targetContainer = listItemContainer;
11880          }
11881        }
11882
11883        // Only add if not already present (avoid duplicates)
11884        if (
11885          !targetContainer.querySelector(".product-reviews-rating") &&
11886          !targetContainer.querySelector(".review-count")
11887        ) {
11888          productItemRatingContainer.classList.add("product-reviews-rating");
11889          targetContainer.appendChild(productItemRatingContainer);
11890        }
11891      }
11892    } catch (error) {
11893      console.error(
11894        `${this.identifier}: Error processing product item ${id}:`,
11895        error
11896      );
11897    }
11898  },
11899  renderRating: function (rating, page = null) {
11900    const uniqueId = Math.floor(Math.random() * 100000);
11901
11902    const div = document.createElement("div");
11903    div.classList.add(...this.config.styling.classes.starsContainer.split(" "));
11904
11905    const ratingDiv = document.createElement("div");
11906    ratingDiv.className = "flex items-center";
11907
11908    // Use productPageSize if on product page, otherwise use default size
11909    const starSize = (page === "product") ? this.config.stars.productPageSize : this.config.stars.size;
11910
11911    for (let starIndex = 1; starIndex <= 5; starIndex++) {
11912      const gradientId = `rating-gradient-${uniqueId}-${starIndex}`;
11913
11914      const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
11915      svg.setAttribute("aria-hidden", "true");
11916      svg.setAttribute("class", starSize);
11917      svg.setAttribute("viewBox", "0 0 20 20");
11918      svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
11919
11920      const title = document.createElementNS(
11921        "http://www.w3.org/2000/svg",
11922        "title"
11923      );
11924      title.textContent = `Star ${starIndex}`;
11925      svg.appendChild(title);
11926
11927      const defs = document.createElementNS(
11928        "http://www.w3.org/2000/svg",
11929        "defs"
11930      );
11931      const linearGradient = document.createElementNS(
11932        "http://www.w3.org/2000/svg",
11933        "linearGradient"
11934      );
11935      linearGradient.setAttribute("id", gradientId);
11936
11937      const remainder = rating - starIndex + 1;
11938      const fillPercentage =
11939        remainder > 0 && remainder < 1
11940          ? remainder * 100
11941          : starIndex <= Math.floor(rating)
11942          ? 100
11943          : 0;
11944
11945      const stop1 = document.createElementNS(
11946        "http://www.w3.org/2000/svg",
11947        "stop"
11948      );
11949      stop1.setAttribute("offset", "0%");
11950      stop1.setAttribute("stop-color", this.config.star.color.active);
11951      linearGradient.appendChild(stop1);
11952
11953      const stop2 = document.createElementNS(
11954        "http://www.w3.org/2000/svg",
11955        "stop"
11956      );
11957      stop2.setAttribute("offset", fillPercentage + "%");
11958      stop2.setAttribute("stop-color", this.config.star.color.active);
11959      linearGradient.appendChild(stop2);
11960
11961      const stop3 = document.createElementNS(
11962        "http://www.w3.org/2000/svg",
11963        "stop"
11964      );
11965      stop3.setAttribute("offset", fillPercentage + "%");
11966      stop3.setAttribute("stop-color", this.config.star.color.inactive);
11967      linearGradient.appendChild(stop3);
11968
11969      const stop4 = document.createElementNS(
11970        "http://www.w3.org/2000/svg",
11971        "stop"
11972      );
11973      stop4.setAttribute("offset", "100%");
11974      stop4.setAttribute("stop-color", this.config.star.color.inactive);
11975      linearGradient.appendChild(stop4);
11976
11977      defs.appendChild(linearGradient);
11978      svg.appendChild(defs);
11979
11980      const path = document.createElementNS(
11981        "http://www.w3.org/2000/svg",
11982        "path"
11983      );
11984      path.setAttribute("fill", `url(#${gradientId})`);
11985      path.setAttribute("d", this.config.star.path);
11986      svg.appendChild(path);
11987
11988      ratingDiv.appendChild(svg);
11989    }
11990
11991    div.appendChild(ratingDiv);
11992
11993    return div;
11994  },
11995  renderForm: function (language) {
11996    const form = document.createElement("form");
11997
11998    form.id = "productReviewForm";
11999    form.style.position = "relative";
12000
12001    form.setAttribute(
12002      "x-on:submit.prevent",
12003      `window.apps.${this.identifier}.onSubmitReview()`
12004    );
12005
12006    form.classList.add(...this.config.styling.classes.form.split(" "));
12007    form.setAttribute("x-show", "showForm");
12008    form.setAttribute("x-transition", "");
12009    form.setAttribute("x-cloak", "");
12010
12011    // Add description if available
12012    if (this.data.reviewSectionDescription) {
12013      const descriptionP = document.createElement("p");
12014      descriptionP.className = "review-section-description text-sm text-gray-700 mb-4";
12015      descriptionP.textContent = this.data.reviewSectionDescription;
12016      form.appendChild(descriptionP);
12017    }
12018
12019    const ratingDiv = document.createElement("div");
12020    ratingDiv.classList.add(
12021      ...this.config.styling.classes.formRatingDiv.split(" ")
12022    );
12023
12024    const ratingLabel = document.createElement("label");
12025    ratingLabel.classList.add(
12026      ...this.config.styling.classes.formRatingLabel.split(" ")
12027    );
12028
12029    ratingLabel.textContent = language?.yourRating ?? "Your rating";
12030    ratingDiv.appendChild(ratingLabel);
12031
12032    ratingDiv.appendChild(this.renderRatingInput());
12033
12034    form.appendChild(ratingDiv);
12035
12036    const nameDiv = document.createElement("div");
12037    nameDiv.classList.add(...this.config.styling.classes.formGroup.split(" "));
12038
12039    const nameLabel = document.createElement("label");
12040    nameLabel.setAttribute("for", "reviewName");
12041    nameLabel.className = "text-sm font-bold text-gray-700";
12042    nameLabel.textContent = language?.name ?? "Name";
12043    nameDiv.appendChild(nameLabel);
12044
12045    const nameInput = document.createElement("input");
12046    nameInput.required = true;
12047    nameInput.type = "text";
12048    nameInput.id = "reviewName";
12049    nameInput.classList.add(
12050      ...this.config.styling.classes.textInput.split(" ")
12051    );
12052    nameDiv.appendChild(nameInput);
12053
12054    form.appendChild(nameDiv);
12055
12056    const emailDiv = document.createElement("div");
12057    emailDiv.classList.add(...this.config.styling.classes.formGroup.split(" "));
12058
12059    const emailLabel = document.createElement("label");
12060    emailLabel.setAttribute("for", "reviewEmail");
12061    emailLabel.className = "text-sm font-bold text-gray-700";
12062    emailLabel.textContent = language?.email ?? "Email";
12063    emailDiv.appendChild(emailLabel);
12064
12065    const emailInput = document.createElement("input");
12066    emailInput.required = true;
12067    emailInput.type = "email";
12068    emailInput.id = "reviewEmail";
12069    emailInput.classList.add(
12070      ...this.config.styling.classes.textInput.split(" ")
12071    );
12072    emailDiv.appendChild(emailInput);
12073
12074    form.appendChild(emailDiv);
12075
12076    const messageDiv = document.createElement("div");
12077    messageDiv.classList.add(
12078      ...this.config.styling.classes.formGroup.split(" ")
12079    );
12080
12081    const messageLabel = document.createElement("label");
12082    messageLabel.setAttribute("for", "reviewMessage");
12083    messageLabel.className = "text-sm font-bold text-gray-700";
12084    messageLabel.textContent = language?.message ?? "Message";
12085    messageDiv.appendChild(messageLabel);
12086
12087    const messageTextarea = document.createElement("textarea");
12088    messageTextarea.required = true;
12089    messageTextarea.id = "reviewMessage";
12090    messageTextarea.rows = 10;
12091    messageTextarea.classList.add(
12092      ...this.config.styling.classes.textInput.split(" ")
12093    );
12094    if (this.data.reviewTextPlaceholder) {
12095      messageTextarea.placeholder = this.data.reviewTextPlaceholder;
12096    }
12097    messageDiv.appendChild(messageTextarea);
12098
12099    form.appendChild(messageDiv);
12100
12101    const submitButton = document.createElement("button");
12102    submitButton.classList.add(
12103      ...this.config.styling.classes.formSubmitButton.split(" ")
12104    );
12105    submitButton.textContent = language?.submitReview ?? "Submit review";
12106    form.appendChild(submitButton);
12107
12108    const errorMessageContainer = document.createElement("div");
12109    errorMessageContainer.classList.add(
12110      ...this.config.styling.classes.formErrorMessageContainer.split(" ")
12111    );
12112    errorMessageContainer.id = "productReviewErrorMessage";
12113
12114    form.appendChild(errorMessageContainer);
12115
12116    return form;
12117  },
12118  renderRatingInput: function () {
12119    const ratingDiv = document.createElement("div");
12120    ratingDiv.className = "flex items-center mb-2";
12121    ratingDiv.setAttribute(
12122      "x-data",
12123      "{ currentRating: null, hoverRating: null }"
12124    );
12125
12126    // Use productPageSize since this is only used on product page forms
12127    const starSize = this.config.stars.productPageSize;
12128
12129    ratingDiv.innerHTML = `
12130<template x-for="starIndex in 5">
12131  <div x-id="['reviewStar']">
12132    <input required class="opacity-0 absolute -z-50" type="radio" :id="$id('reviewStar')" name="rating" :value="starIndex" x-model="currentRating" />
12133  
12134    <label 
12135      :for="$id('reviewStar')" 
12136      @mouseover="hoverRating = starIndex"
12137      @mouseleave="hoverRating = null"
12138    >
12139    <svg aria-hidden="true" 
12140      :style="{color: !hoverRating && currentRating >= starIndex || hoverRating >= starIndex ? '${this.config.star.color.active}' : '${this.config.star.color.inactive}'}"
12141      class="${starSize}" 
12142      fill="currentColor" 
12143      viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title x-text="['One star', 'Two stars', 'Three stars', 'Four stars', 'Five stars'][starIndex - 1]"></title><path d="${this.config.star.path}"></path>
12144    </svg>
12145
12146  </label>
12147  </div>
12148</template>
12149    `;
12150
12151    return ratingDiv;
12152  },
12153  onSubmitReview: function () {
12154    const errorMessageContainer = document.getElementById(
12155      "productReviewErrorMessage"
12156    );
12157
12158    try {
12159      this.submitReview();
12160      errorMessageContainer.textContent = "";
12161    } catch (error) {
12162      this.handleError(error);
12163    }
12164  },
12165  handleError: function (error) {
12166    console.error("QuickbutikProductReviews@handleError", error);
12167
12168    const errorMessageContainer = document.getElementById(
12169      "productReviewErrorMessage"
12170    );
12171
12172    const errorMessage = `An error occurred${
12173      error ? `: ${JSON.stringify(error, null, 2)}` : ""
12174    }.`;
12175
12176    errorMessageContainer.textContent = errorMessage;
12177  },
12178  submitReview: function () {
12179    const data = [
12180      {
12181        key: "product_id",
12182        value: document
12183          .querySelector("[data-product-id]")
12184          .getAttribute("data-product-id"),
12185      },
12186      {
12187        key: "name",
12188        value: document.getElementById("reviewName").value,
12189      },
12190      {
12191        key: "email",
12192        value: document.getElementById("reviewEmail").value,
12193      },
12194      {
12195        key: "rating",
12196        value: document.querySelector('input[name="rating"]:checked').value,
12197      },
12198      {
12199        key: "message",
12200        value: document.getElementById("reviewMessage").value,
12201      },
12202    ];
12203
12204    const formData = new FormData();
12205
12206    data.forEach(({ key, value }) => {
12207      if (!value) {
12208        throw new Error(`Missing ${key}`);
12209      }
12210
12211      formData.append(key, value);
12212    });
12213
12214    fetch("/apps/productreviews/add", {
12215      method: "POST",
12216      body: formData,
12217    })
12218      .then((response) => {
12219        if (!response.ok) {
12220          this.handleError(response.statusText);
12221        }
12222        return response.json();
12223      })
12224      .then((data) => {
12225        if (data.success === 1) {
12226          this.renderThankYouMessage(data);
12227        } else {
12228          this.handleError(data?.error);
12229        }
12230      })
12231      .catch((error) => {
12232        this.handleError(error);
12233      });
12234  },
12235  renderThankYouMessage: function (data) {
12236    const { message_lbl, message_txt } = data;
12237
12238    const thankYouDiv = document.createElement("div");
12239    thankYouDiv.classList.add(
12240      ...this.config.styling.classes.formThankYouMessageContainer.split(" ")
12241    );
12242
12243    const thankYouLabel = document.createElement("label");
12244    thankYouLabel.classList.add(
12245      ...this.config.styling.classes.formThankYouMessageLabel.split(" ")
12246    );
12247    thankYouLabel.textContent = message_lbl;
12248    thankYouDiv.appendChild(thankYouLabel);
12249
12250    const thankYouMessage = document.createElement("p");
12251    thankYouMessage.classList.add(
12252      ...this.config.styling.classes.formThankYouMessageLabel.split(" ")
12253    );
12254    thankYouMessage.textContent = message_txt;
12255    thankYouDiv.appendChild(thankYouMessage);
12256
12257    const form = document.getElementById("productReviewForm");
12258
12259    form.parentNode.replaceChild(thankYouDiv, form);
12260
12261    setTimeout(() => {
12262      thankYouDiv.parentNode.removeChild(thankYouDiv);
12263
12264      const productReviewButton = document.getElementById(
12265        "productReviewShowFormButton"
12266      );
12267
12268      if (productReviewButton) {
12269        productReviewButton.parentNode.removeChild(productReviewButton);
12270      }
12271    }, 5000);
12272  },
12273  renderErrorMessage: function (error) {
12274    const errorDiv = document.querySelector("#productReviewErrorMessage");
12275    errorDiv.textContent = error || "Something went wrong"; // TODO: Translate
12276  },
12277});
12278</script>
vendor: 1 bytes, line 12278
12278
12279<script>
12280    qb_cookieconsent.run({
12281        current_lang: 'en',
12282        autoclear_cookies: true,
12283        cookie_expiration: 90,
12284        autoload_css: true,
12285        theme_css: '/assets/shopassets/cookieconsent/cookieconsent.css?v=231',
12286        languages: {
12287            en: {
12288                consent_modal: {
12289                    title: "Vi använder cookies",
12290                    description: 'Vecanis använder cookies - inte från ugnen, utan de digitala. Genom att fortsätta på sidan godkänner du användandet av cookies.',
12291                    primary_btn: {
12292                        text: 'Godkänn alla',
12293                        role: 'accept_all'
12294                    },
12295                    secondary_btn: {
12296                        text: 'Inställningar',
12297                        role: 'settings'
12298                    }
12299                },
12300                settings_modal: {
12301                    title: 'Cookie inställningar',
12302                    save_settings_btn: "Spara",
12303                    accept_all_btn: "Godkänn alla",
12304                    cookie_table_headers: [
12305                        { col1: "Name" },
12306                        { col2: "Domain" },
12307                        { col3: "Expiration" },
12308                        { col4: "Description" }
12309                    ],
12310                    blocks: qb_cookieconsent_blocks
12311                }
12312            }
12313        },
12314        onAccept: function (cookie) {
12315            var event = new CustomEvent("QB_CookieConsentAccepted", {
12316                detail: {
12317                    cookie: cookie
12318                }
12319            });
12320
12321            document.dispatchEvent(event);
12322        },
12323        onChange: function (cookie) {
12324            var event = new CustomEvent("QB_CookieConsentAccepted", {
12325                detail: {
12326                    cookie: cookie
12327                }
12328            });
12329
12330            document.dispatchEvent(event);
12331        },
12332    });
12333
12334</script>
12334
12335
12336<script type="text/javascript">// Autocomplete Search App loaded
12337</script>
12337<script type="text/javascript">window.registerApp({
12338    name: 'Recaptcha',
12339    identifier: 'QuickbutikRecaptcha',
12340    description: 'Recaptcha',
12341    version: '1.1.0',
12342    pages: ['contact'],
12343    data: {},
12344    config: {},
12345    onMount: function ({ appData, page }) {
12346        console.debug(`${this.identifier}@onMount`)
12347
12348        let form = document.querySelector('form[role=form]')
12349        if (!form) {
12350            console.error('Aborting recaptcha, form not found.')
12351            return
12352        }
12353
12354        const createCaptchaDiv = () => {
12355            let captchaDiv = document.createElement('div');
12356            captchaDiv.className = 'g-recaptcha'
12357            captchaDiv.dataset.sitekey = recaptchaSiteKey
12358            captchaDiv.style.marginBottom = '15px'
12359            captchaDiv.style.overflow = 'hidden'
12360            return captchaDiv
12361        }
12362                   
12363        let button = form.querySelector('button');
12364        let inputSubmit = form.querySelector('input[type=submit]');
12365        if (button) {
12366            button.insertAdjacentElement('beforebegin', createCaptchaDiv())
12367        } else if (inputSubmit) {
12368            inputSubmit.insertAdjacentElement('beforebegin', createCaptchaDiv())
12369        }
12370
12371        // Honeypot for contact page
12372        let contactForm = document.querySelector('form[role=form]#contact-form')
12373        if (contactForm) {
12374            let pottyDiv = document.createElement('div')
12375            pottyDiv.style.position = 'absolute'
12376            pottyDiv.style.left = '-5000px'
12377            pottyDiv.setAttribute('aria-hidden', true)
12378
12379            let codeLabel = document.createElement('label')
12380            codeLabel.innerText = 'Code'
12381            codeLabel.for = 'code'
12382            codeLabel.className = contactForm.querySelector('label').className
12383            codeLabel.setAttribute('aria-hidden', true)
12384            pottyDiv.appendChild(codeLabel)
12385
12386            let codeInput = document.createElement('input')
12387            codeInput.type = 'text'
12388            codeInput.id = 'code'
12389            codeInput.name = 'code'
12390            codeInput.tabIndex = -1
12391            codeInput.autocomplete = 'off'
12392            codeInput.className = contactForm.querySelector('input[name="name"]').className
12393            codeInput.setAttribute('aria-hidden', true)
12394            pottyDiv.appendChild(codeInput)
12395
12396            contactForm.querySelector('textarea').insertAdjacentElement('afterend', pottyDiv)
12397        }
12398
12399        // Inject Recaptcha script
12400        let existingScript = document.querySelector('script[src="https://www.google.com/recaptcha/api.js"]')
12401        if (!existingScript) {
12402            let script = document.createElement('script')
12403            script.src = 'https://www.google.com/recaptcha/api.js'
12404            script.type = 'text/javascript'
12405            script.async = true
12406            document.querySelector('head').appendChild(script)
12407        }
12408
12409    },
12410})
12411</script>
12411    
12411<script src="/assets/shopassets/misc/magicbean.js?v=3" data-shop-id="2666" data-url="https://magic-bean.services.quickbutik.com/v1/sow" data-site-section="1" data-key="f35e6c2bb99ee24531c52131069484366fdf8dfe417219e9b2a429657c71446c" data-timestamp="1791438379" defer></script>
12411<script>
12412    document.addEventListener('QB_CookieConsentAccepted', function(event) {
12413        const cookie = qbGetEvent(event).cookie;
12414
12415        const getConsentLevel = (level) => (cookie && cookie.level && cookie.level.includes(level)) ? 'granted' : 'denied';
12416
12417        gtag('consent', 'update', {
12418            'analytics_storage': getConsentLevel('analytics'),
12419            'ad_storage': getConsentLevel('marketing'),
12420            'ad_user_data': getConsentLevel('marketing'),
12421            'ad_personalization': getConsentLevel('marketing'),
12422        });
12423    });
12424
12425    document.addEventListener('QB_CartAdded', function(e) {
12426        const itemData = qbGetEvent(e).itemData;
12427
12428        // item.id is the CART-LINE key &mdash; an md5 of the line (see
12429        // Model_Shop_Basketnew), unique per product+variant+options and
12430        // meaningless outside this basket. item.raw_id is the product id,
12431        // which is what view_item sends, what <g:id> carries in the Google
12432        // Shopping feed, and what GA4 needs to join the item funnel.
12433        let eventData = {
12434            currency: 'SEK',
12435            value: parseFloat(itemData.item.price_raw),
12436            items: [{
12437                id: itemData.item.raw_id || itemData.item.id,
12438                item_id: itemData.item.raw_id || itemData.item.id,
12439                name: itemData.item.title.replaceAll('"', ''),
12440                item_name: itemData.item.title.replaceAll('"', ''),
12441                affiliation: `Vecanis`,
12442                ...(itemData.variant_name && {
12443                    name: itemData.item.title.replaceAll('"', '') + ' ' + itemData.variant_name.replaceAll('"', ''),
12444                    item_name: itemData.item.title.replaceAll('"', '') + ' ' + itemData.variant_name.replaceAll('"', ''),
12445                    variant: itemData.variant_name.replaceAll('"', ''),
12446                    item_variant: itemData.variant_name.replaceAll('"', ''),
12447                }),
12448                ...(itemData.item.item_category && {
12449                    item_category: itemData.item.item_category.replaceAll('"', ''),
12450                }),
12451                price: parseFloat(itemData.item.price_raw),
12452                quantity: itemData.qty
12453            }],
12454        };
12455        'undefined' !== typeof gtag && gtag('event', 'add_to_cart', eventData);
12456    });
12457</script>
12457<script type="text/javascript">
12458 if(qb_cookieconsent.allowedCategory('analytics'))
12459 {
12460    !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
12461    posthog.init('phc_mPzWNLM3JN9i34tiiQ6LlKfr6uoMArp0NOwHdMLOg63',{api_host:'https://eu.i.posthog.com'})
12462 }
12463 </script>
12463<script id="mcjs">if (typeof qb_cookieconsent == 'undefined' || qb_cookieconsent.allowedCategory('marketing')) { !function(c,h,i,m,p){m=c.createElement(h),p=c.getElementsByTagName(h)[0],m.async=1,m.src=i,p.parentNode.insertBefore(m,p)}(document,"script","https://chimpstatic.com/mcjs-connected/js/users/172f2db97a5ff2a4bd7008db8/c92311638d4901fb3786b162f.js"); } </script>
12463<script>;(function () {
12464    if (window.parent === window) return;
12465
12466    const urlParams = new URLSearchParams(window.location.search);
12467    if (urlParams.has('_qbdev')) {
12468        try { sessionStorage.setItem('_qbdev', '1'); } catch(e) {}
12469    }
12470    try { if (!sessionStorage.getItem('_qbdev')) return; } catch(e) { return; }
12471
12472    const report = () => {
12473        try {
12474            const params = new URLSearchParams(window.location.search);
12475            params.delete('_qbdev');
12476            params.delete('_qbtid');
12477            const qs = params.toString();
12478            window.parent.postMessage(
12479                { type: 'navigation', path: window.location.pathname + (qs ? '?' + qs : '') },
12480                '*'
12481            );
12482        } catch (e) {}
12483    };
12484
12485    report();
12486
12487    const { pushState, replaceState } = history;
12488    history.pushState = function (...args) {
12489        const result = pushState.apply(this, args);
12490        report();
12491        return result;
12492    };
12493    history.replaceState = function (...args) {
12494        const result = replaceState.apply(this, args);
12495        report();
12496        return result;
12497    };
12498    window.addEventListener('popstate', report);
12499})()</script>
vendor: 1 bytes, line 12499
12499
12500<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4bc70e2c01a94c73b74392e4234840661791215815920" integrity="sha512-L0ha0OXavK/8okipN9F8BtP84dg9DUhPERbBXzwI6dgTA55d2+yweo3pn5CSFYs45/r8md2+xvUPtTdvTNRfjA==" data-cf-beacon='{"version":"2024.11.0","token":"7a304e0c4e854d16bca3b20c50bbf8db","spa":2}' crossorigin="anonymous"></script>
12500
12501</body>
12502
12503</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.