PageSourceSearch

https://lave.live/online-store

html lave.live collected 2026-10-02 01:59:35 UTC 157,947 bytes, 3,278 lines download raw bytes

1<!DOCTYPE html>
2
3<html lang="en"
4    dir="ltr">
5
6<head>
7
8    <link rel="stylesheet" data-navigate-track
9        href="https://lave.live/css/localization.css?direction=ltr&v=3">
10    <meta name="csrf-token" content="AjMCTFOftFon9p8sIzSEEUc4qAhUkKFeGD31X2bH">
11    <meta charset="utf-8">
12    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no" />
13
14
15    <title>Create an Online Store in 2 Minutes | Lave</title>
16<meta name="description" content="Launch a beautiful online store in minutes with Lave. Sell on social media, receive WhatsApp orders, accept payments, and manage products easily.">
17<link rel="canonical" href="https://lave.live/online-store">
18<meta property="og:type" content="website">
19<meta property="og:locale" content="en_US">
20<meta property="og:site_name" content="Lave">
21<meta property="og:url" content="https://lave.live/online-store">
22<meta property="og:title" content="Create an Online Store in 2 Minutes | Lave">
23<meta property="og:description" content="Launch a beautiful online store in minutes with Lave. Sell on social media, receive WhatsApp orders, accept payments, and manage products easily.">
24<meta property="og:image" content="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/opengraph/index.jpg">
25<meta name="twitter:card" content="summary_large_image">
26<meta name="twitter:site" content="@laveappofficial">
27<meta name="twitter:title" content="Create an Online Store in 2 Minutes | Lave">
28<meta name="twitter:description" content="Launch a beautiful online store in minutes with Lave. Sell on social media, receive WhatsApp orders, accept payments, and manage products easily.">
29<meta name="twitter:image" content="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/opengraph/index.jpg">
30<link rel="alternate" hreflang="x-default" href="https://lave.live/online-store">
31<link rel="alternate" hreflang="en" href="https://lave.live/online-store">
32<link rel="alternate" hreflang="fr" href="https://lave.live/fr/online-store">
33<link rel="alternate" hreflang="ar" href="https://lave.live/ar/online-store">
34<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","name":"Lave","url":"https://lave.live"}</script>
vendor: 1 bytes, line 34
34
35<script type="application/ld+json">{"@context":"https://schema.org","@type":"Organization","name":"Lave","url":"https://lave.live","logo":"https://lave.live/images/icons/icon-512x512.png"}</script>
vendor: 1 bytes, line 35
35
36<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","name":"Create an Online Store in 2 Minutes | Lave","description":"Launch a beautiful online store in minutes with Lave. Sell on social media, receive WhatsApp orders, accept payments, and manage products easily.","image":"https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/opengraph/index.jpg"}</script>
36
37    <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon/favicon-32x32.png?v=1.0">
38    <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon/favicon-16x16.png?v=1.0">
39    <link rel="shortcut icon" href="/images/favicon/favicon-32x32.png?v=1.0">
40    <link rel="preconnect" href="https://im.zoho.com">
41    <link rel="preconnect" href="https://static.zohocdn.com">
42    <link rel="preconnect" href="https://desk.zoho.com">
43    <link rel="dns-prefetch" href="//im.zoho.com">
44    <link rel="dns-prefetch" href="//static.zohocdn.com">
45    <link rel="dns-prefetch" href="//desk.zoho.com">
46
47
48
49    
50    <!-- Web Application Manifest -->
51
52    <link rel="manifest" href="/manifest.json">
53
54    <!-- Chrome for Android theme color -->
55    <meta name="theme-color" content="white">
56
57    <!-- Add to homescreen for Chrome on Android -->
58    <meta name="mobile-web-app-capable" content="yes">
59    <meta name="application-name" content="Lave">
60    <link rel="icon" sizes="512x512" href="/images/icons/icon-512x512.png?v=1.0">
61
62    <!-- Add to homescreen for Safari on iOS -->
63    <meta name="apple-mobile-web-app-capable" content="yes">
64    <meta name="apple-mobile-web-app-status-bar-style" content="white">
65    <meta name="apple-mobile-web-app-title" content="Lave">
66    <link rel="apple-touch-icon" href="/images/icons/icon-512x512.png?v=1.0">
67
68    
68<script src="https://cdn.jsdelivr.net/npm/[email protected]/cdn.min.js"></script>
68
69
70    
70<script>
71        if (typeof window.iosPWASplash === 'function') {
72            window.iosPWASplash("/images/icons/icon-512x512.png?v=1.0", '#EC4899');
73        }
74    </script>
74
75
76    <!-- Tile for Win8 -->
77    <meta name="msapplication-TileColor" content="blue">
78    <meta name="msapplication-TileImage" content="/images/icons/icon-512x512.png?v=1.0">
79
80    
81
82    <link rel="stylesheet"
83        href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/uikit.min.css?v=3" />
84
85    
85<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/uikit.min.js"></script>
85
86    
86<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/uikit-icons.min.js"></script>
86
87
88    
88<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
88
89
90
91    <link rel="stylesheet"
92        href="https://lave.live/css/tailwind-full.css?v=1790843855-3">
93
94    
95    <style>
96        .uk-button:not(.uk-button-link):not(.uk-button-text):disabled,
97        .uk-button:not(.uk-button-link):not(.uk-button-text):disabled:hover {
98            background-color: #f9fafb;
99            border-color: #e5e7eb;
100            /* Ring, because a bare .uk-button has no border of its own to colour. */
101            box-shadow: 0 0 0 1px #e5e7eb;
102            color: #6b7280;
103        }
104    </style>
105
106    <!-- Date Range Picker CSS -->
107    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/daterangepicker.css?v=3">
108    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tom-select/dist/css/tom-select.css?v=3">
109    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/driver.js/dist/driver.css?v=3">
110
111    <link
112        href="https://fonts.googleapis.com/css2?family=Acme&family=Aladin&family=Inter&family=Poppins&family=Roboto&family=Manrope&family=Space+Grotesk&family=Plus+Jakarta+Sans&family=Sora&family=Open+Sans&family=Lato&family=Ubuntu&family=Nunito&family=Work+Sans&family=Syne&family=Montserrat&display=swap&v=3"
113        rel="stylesheet">
114
115    <link rel="preconnect" href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;500;600;700&display=swap"
116        rel="stylesheet">
117
118    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css?v=3">
119
120
121    
121<script src="https://unpkg.com/[email protected]"></script>
121
122
123    
123<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/hugerte.min.js"></script>
123
124    <meta name="facebook-domain-verification" content="zylazptgkc1z0arghjheromt5oq39m" />
125    <style>
126        html {
127            overscroll-behavior: none;
128            background-color: #f9fafb;
129            scrollbar-gutter: stable;
130        }
131
132        body {
133            font-family: 'Inter', 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
134        }
135
136
137        @font-face {
138            font-family: 'Nova Mono';
139            src: url('https://lave.live/fonts/nova-mono.ttf') format('truetype');
140            font-weight: normal;
141            font-style: normal;
142        }
143
144        .font-nova-mono {
145            font-family: 'Nova Mono', monospace !important;
146        }
147
148        /* Responsive page header spacing */
149        .page-header {
150            padding-top: 2.5rem;
151            /* pt-10 for mobile */
152        }
153
154        @media (min-width: 768px) {
155            .page-header {
156                padding-top: 5rem;
157                /* pt-20 for tablets and laptops */
158            }
159        }
160
161
162        .uk-notification {
163            z-index: 100 !important;
164        }
165    </style>
166
167                    <!-- Meta Pixel Code -->
168        
168<script>
169            ! function(f, b, e, v, n, t, s) {
170                if (f.fbq) return;
171                n = f.fbq = function() {
172                    n.callMethod ?
173                        n.callMethod.apply(n, arguments) : n.queue.push(arguments)
174                };
175                if (!f._fbq) f._fbq = n;
176                n.push = n;
177                n.loaded = !0;
178                n.version = '2.0';
179                n.queue = [];
180                t = b.createElement(e);
181                t.async = !0;
182                t.src = v;
183                s = b.getElementsByTagName(e)[0];
184                s.parentNode.insertBefore(t, s)
185            }(window, document, 'script',
186                'https://connect.facebook.net/en_US/fbevents.js');
187            window.laveMetaAdvancedMatchingData = {};
188            window.laveMetaPixelId = "950672977777178";
189            window.laveMetaPixelOptions = {"testEventCode":"TEST52976"};
190            window.laveApplyMetaAdvancedMatching = function(source) {
191                if (typeof fbq === 'undefined' || !window.laveMetaPixelId) {
192                    return;
193                }
194
195                const data = {};
196
197                if (source instanceof HTMLFormElement) {
198                    const email = source.querySelector('[name="email"]')?.value?.trim().toLowerCase();
199
200                    if (email) {
201                        data.em = email;
202                    }
203                } else if (source && typeof source === 'object') {
204                    Object.assign(data, source);
205                }
206
207                if (!data.em) {
208                    return;
209                }
210
211                const signature = JSON.stringify(data);
212                if (window.laveMetaAdvancedMatchingSignature === signature) {
213                    return;
214                }
215
216                window.laveMetaAdvancedMatchingSignature = signature;
217                window.laveMetaAdvancedMatchingData = data;
218
219                if (window.laveMetaPixelOptions) {
220                    fbq('init', window.laveMetaPixelId, data, window.laveMetaPixelOptions);
221                } else {
222                    fbq('init', window.laveMetaPixelId, data);
223                }
224            };
225                            fbq('init', window.laveMetaPixelId, window.laveMetaAdvancedMatchingData, window.laveMetaPixelOptions);
226                    </script>
226
227        <!-- End Meta Pixel Code -->
228    
229            <!-- Google Tag Manager -->
230        
230<script>
vendor: 610 bytes, lines 230-244
230
231            (function(w, d, s, l, i) {
232                w[l] = w[l] || [];
233                w[l].push({
234                    'gtm.start': new Date().getTime(),
235                    event: 'gtm.js'
236                });
237                var f = d.getElementsByTagName(s)[0],
238                    j = d.createElement(s),
239                    dl = l != 'dataLayer' ? '&l=' + l : '';
240                j.async = true;
241                j.src =
242                    'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
243                f.parentNode.insertBefore(j, f);
244            })(window, document, 'script', 'dataLayer', '
244GTM-WFZBP7S3
vendor: 12 bytes, lines 244-245
244');
245        
245</script>
245
246        <!-- End Google Tag Manager -->
247        <!-- Livewire Styles --><style >[wire\:loading][wire\:loading], [wire\:loading\.delay][wire\:loading\.delay], [wire\:loading\.list-item][wire\:loading\.list-item], [wire\:loading\.inline-block][wire\:loading\.inline-block], [wire\:loading\.inline][wire\:loading\.inline], [wire\:loading\.block][wire\:loading\.block], [wire\:loading\.flex][wire\:loading\.flex], [wire\:loading\.table][wire\:loading\.table], [wire\:loading\.grid][wire\:loading\.grid], [wire\:loading\.inline-flex][wire\:loading\.inline-flex] {display: none;}[wire\:loading\.delay\.none][wire\:loading\.delay\.none], [wire\:loading\.delay\.shortest][wire\:loading\.delay\.shortest], [wire\:loading\.delay\.shorter][wire\:loading\.delay\.shorter], [wire\:loading\.delay\.short][wire\:loading\.delay\.short], [wire\:loading\.delay\.default][wire\:loading\.delay\.default], [wire\:loading\.delay\.long][wire\:loading\.delay\.long], [wire\:loading\.delay\.longer][wire\:loading\.delay\.longer], [wire\:loading\.delay\.longest][wire\:loading\.delay\.longest] {display: none;}[wire\:offline][wire\:offline] {display: none;}[wire\:dirty]:not(textarea):not(input):not(select) {display: none;}:root {--livewire-progress-bar-color: rgb(231, 20, 111);}[x-cloak="x-cloak"],[x-cloak=""] {display: none !important;}[wire\:cloak="wire:cloak"],[wire\:cloak=""] {display: none !important;}dialog#livewire-error::backdrop {background-color: rgba(0, 0, 0, .6);}</style>
248    <style>
249            .animate-flash {
250                position: relative;
251                overflow: hidden;
252                isolation: isolate;
253            }
254
255            .animate-flash::before {
256                content: '';
257                position: absolute;
258                inset: 0;
259                z-index: -1;
260                pointer-events: none;
261                background: linear-gradient(110deg, transparent 30%, currentColor 50%, transparent 70%);
262                opacity: 0.5;
263                transform: translateX(-120%);
264                animation: lave-button-shine 3s ease-in-out infinite;
265            }
266
267            @keyframes lave-button-shine {
268                0%, 40% { transform: translateX(-120%); }
269                90%, 100% { transform: translateX(120%); }
270            }
271
272            @media (prefers-reduced-motion: reduce) {
273                .animate-flash::before { animation: none; }
274            }
275        </style>
276    
276<script defer src="https://lave.live/vendor/thumbmarkjs/thumbmark.umd.js"></script>
276
277    
277<script defer src="https://lave.live/js/browser-fingerprint.js"></script>
277
278</head>
279
280
281<body>
282    
283            
283<script>
284            (() => {
285                // Generate on display, not render: prefetched/history pages need a fresh ID per visit.
286                const eventId = crypto.randomUUID();
287                if (typeof fbq === 'function') {
288                    fbq('trackSingle', "950672977777178", 'PageView', {}, { eventID: eventId });
289                }
290            })();
291        </script>
291
292    
293    <!-- Live wire progress bar -->
294    <style>
295        #nprogress .bar {
296            height: 5px;
297            background: rgb(231, 20, 111);
298        }
299    </style>
300
301            <noscript><img height="1" width="1" style="display:none"
302                src="https://www.facebook.com/tr?id=950672977777178&ev=PageView&noscript=1" /></noscript>
303    
304            <!-- Google Tag Manager (noscript) -->
vendor: 76 bytes, lines 304-305
304
305        <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
305GTM-WFZBP7S3
vendor: 107 bytes, lines 305-307
305"
306                height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
307        
307<!-- End Google Tag Manager (noscript) -->
308    
309    
309<script src="https://cdnjs.cloudflare.com/ajax/libs/pulltorefreshjs/0.1.22/index.umd.min.js"></script>
309
310
311    
312    
312<script src="https://cdn.jsdelivr.net/npm/tom-select/dist/js/tom-select.complete.min.js"></script>
312
313
314
315    
315<script src="https://cdn.jsdelivr.net/npm/[email protected]/tsparticles.confetti.bundle.min.js"></script>
315
316
317    
317<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
317
318    
318<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
318
319    
319<script src="https://cdn.jsdelivr.net/npm/driver.js/dist/driver.js.iife.js"></script>
319
320
321
322
323
324    <!-- lave brand color = #E7146F -->
325    <div class="progress" role="progressbar" aria-label="Loading" style="height: 5px; background-color: white;">
326        <div class="indeterminate" style="background-color:  rgb(20, 55, 231);"></div>
327    </div>
328
329    <style>
330        .progress {
331            position: fixed;
332            top: 0;
333            height: 15px;
334            width: 100%;
335            border-radius: 8px;
336            background-clip: padding-box;
337            overflow: hidden;
338            z-index: 100;
339        }
340
341        .progress .indeterminate:before {
342            content: "";
343            position: absolute;
344            background-color: inherit;
345            top: 0;
346            left: 0;
347            bottom: 0;
348            will-change: left, right;
349            -webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
350            animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
351            z-index: 100;
352        }
353
354        .progress .indeterminate:after {
355            content: "";
356            position: absolute;
357            background-color: inherit;
358            top: 0;
359            left: 0;
360            bottom: 0;
361            will-change: left, right;
362            -webkit-animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
363            animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
364            -webkit-animation-delay: 1.15s;
365            animation-delay: 1.15s;
366            z-index: 100;
367        }
368
369        .progress {
370            display: none;
371        }
372
373        @-webkit-keyframes indeterminate {
374            0% {
375                left: -35%;
376                right: 100%;
377            }
378
379            60% {
380                left: 100%;
381                right: -90%;
382            }
383
384            100% {
385                left: 100%;
386                right: -90%;
387            }
388        }
389
390        @keyframes indeterminate {
391            0% {
392                left: -35%;
393                right: 100%;
394            }
395
396            60% {
397                left: 100%;
398                right: -90%;
399            }
400
401            100% {
402                left: 100%;
403                right: -90%;
404            }
405        }
406
407        @-webkit-keyframes indeterminate-short {
408            0% {
409                left: -200%;
410                right: 100%;
411            }
412
413            60% {
414                left: 107%;
415                right: -8%;
416            }
417
418            100% {
419                left: 107%;
420                right: -8%;
421            }
422        }
423
424        @keyframes indeterminate-short {
425            0% {
426                left: -200%;
427                right: 100%;
428            }
429
430            60% {
431                left: 107%;
432                right: -8%;
433            }
434
435            100% {
436                left: 107%;
437                right: -8%;
438            }
439        }
440    </style>
441
442
443    
444    <div wire:snapshot="{&quot;data&quot;:[],&quot;memo&quot;:{&quot;id&quot;:&quot;pTZTWTyg9E8dv9pvMHnf&quot;,&quot;name&quot;:&quot;pages::landing.features.online-store&quot;,&quot;path&quot;:&quot;online-store&quot;,&quot;method&quot;:&quot;GET&quot;,&quot;release&quot;:&quot;a-a-a&quot;,&quot;children&quot;:[],&quot;scripts&quot;:[&quot;166337323-0&quot;],&quot;assets&quot;:[],&quot;errors&quot;:[],&quot;locale&quot;:&quot;en&quot;,&quot;islands&quot;:[]},&quot;checksum&quot;:&quot;040f1b5871725a4221cea7c0ad2f78eaf0beab6fe6813c6f436f488260d08c01&quot;}" wire:effects="{&quot;scripts&quot;:{&quot;166337323-0&quot;:&quot;                    &lt;script&gt;\n                        function initializeVideo() {\n                            var video = document.getElementById(&#039;videoElement&#039;);\n                            var videospinner = document.getElementById(&#039;videospinner&#039;);\n                            if (!video || !videospinner || video.dataset.lifecycleBound === &#039;1&#039;) return;\n                            video.dataset.lifecycleBound = &#039;1&#039;;\n\n                            video.addEventListener(&#039;loadeddata&#039;
444, function() {\n                                videospinner.style.display = &#039;none&#039;;\n                            });\n\n                            video.addEventListener(&#039;error&#039;, function() {\n                                videospinner.style.display = &#039;none&#039;; \/\/ Hide videospinner if video fails to load\n                            });\n                        }\n\n                        initializeVideo();\n                    &lt;\/script&gt;\n                        &quot;}}" wire:id="pTZTWTyg9E8dv9pvMHnf" wire:name="pages::landing.features.online-store">
445    <div class="sticky top-0" style="z-index: 15;">
446
447    <header class="hidden bg-white shadow-lg lg:block">
448        <div class="mx-auto flex max-w-7xl items-center justify-between px-6 py-3">
449            <!-- Logo -->
450            <a href="https://lave.live" wire:navigate class="flex items-center" style="text-decoration: none;">
451                <span class="text-4xl font-semibold" style="font-family: 'Nova Mono'; color: #eb487f;">lave</span>
452            </a>
453
454
455            <!-- Nav Links -->
456            <nav class="hidden space-x-6 rtl:space-x-reverse text-sm font-medium text-gray-700 lg:flex">
457                <!-- Features Dropdown -->
458                <div x-data="{ dropdownOpen: false }" x-on:click.outside="dropdownOpen = false"
459                    x-on:keydown.escape.window="dropdownOpen = false" class="dropdown-group relative">
460                    <button type="button" x-on:click="dropdownOpen = ! dropdownOpen"
461                        x-bind:aria-expanded="dropdownOpen" aria-haspopup="true"
462                        class="dropdown-btn flex items-center hover:text-pink-600">
463                        Features
464                        <svg class="ms-1 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
465                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
466                        </svg>
467                    </button>
468                    <div x-show="dropdownOpen" x-cloak
469                        class="dropdown-menu absolute start-0 z-20 mt-3 min-w-[300px] rounded-lg bg-white p-6 shadow-xl transition-all duration-300">
470                        <ul class="space-y-2 text-sm text-gray-700">
471                            <li><a wire:navigate href="https://lave.live/features" class="block hover:text-pink-500">All Features</a></li>
472                            <li><a wire:navigate href="https://lave.live/record-sales" class="block hover:text-pink-500">Sales, Expenses &amp; Debts</a></li>
473                            <li><a wire:navigate href="https://lave.live/online-store" class="block hover:text-pink-500">Online Store</a></li>
474                            <li><a wire:navigate href="https://lave.live/whatsapp-orders" class="block hover:text-pink-500">WhatsApp Orders</a></li>
475                            <li><a wire:navigate href="https://lave.live/payments" class="block hover:text-pink-500">Payments</a></li>
476                            <li><a wire:navigate href="https://lave.live/inventory" class="block hover:text-pink-500">Products &amp; Inventory</a></li>
477                            <li><a wire:navigate href="https://lave.live/marketing" class="block hover:text-pink-500">Marketing Tools</a></li>
478                            <li><a wire:navigate href="https://lave.live/plugins" class="block hover:text-pink-500">Plugins &amp; Integrations</a></li>
479                            <li class="border-t border-gray-200 pt-3 text-xs font-bold uppercase tracking-wider text-gray-400">More from Lave</li>
480                            <li><a wire:navigate href="https://lave.live/advertise" class="block hover:text-pink-500">Advertise Your Brand</a></li>
481                            <li><a wire:navigate href="https://lave.live/taskers" class="block hover:text-pink-500">Become a Tasker</a></li>
482                            <li><a wire:navigate href="https://lave.live/refer-and-earn" class="block hover:text-pink-500">Refer &amp; Earn</a></li>
483                            
484                        </ul>
485                    </div>
486                </div>
487
488                <!-- Pricing -->
489                <a wire:navigate href="https://lave.live/pricing" class="hover:text-pink-600">Pricing</a>
490
491                <!-- resources Dropdown -->
492                <div x-data="{ dropdownOpen: false }" x-on:click.outside="dropdownOpen = false"
493                    x-on:keydown.escape.window="dropdownOpen = false" class="dropdown-group relative">
494                    <button type="button" x-on:click="dropdownOpen = ! dropdownOpen"
495                        x-bind:aria-expanded="dropdownOpen" aria-haspopup="true"
496                        class="dropdown-btn flex items-center hover:text-pink-600">
497                        Resources
498                        <svg class="ms-1 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
499                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
500                        </svg>
501                    </button>
502                    <div x-show="dropdownOpen" x-cloak
503                        class="dropdown-menu absolute start-0 z-20 mt-3 w-48 rounded-lg bg-white p-6 shadow-xl transition-all duration-300">
504                        <ul class="space-y-2 text-sm text-gray-700">
505                            <li><a wire:navigate href="https://lave.live/blog" class="block hover:text-pink-500">Blog</a>
506                            </li>
507                            <li><a wire:navigate href="https://lave.live/help" class="block hover:text-pink-500">Help</a>
508                            </li>
509                            <li><a wire:navigate href="https://lave.live/updates"
510                                    class="block hover:text-pink-500">Updates</a></li>
511                            <li><a wire:navigate href="https://lave.live/faqs" class="block hover:text-pink-500">FAQs</a>
512                            </li>
513                            <li><a href="https://www.youtube.com/@lavedotlive" target="_blank">Videos</a></li>
514                            <li><a wire:navigate href="https://lave.live/contact"
515                                    class="block hover:text-pink-500">Contact</a></li>
516                            <li><a wire:navigate href="https://lave.live/about"
517                                    class="block hover:text-pink-500">About</a></li>
518                        </ul>
519                    </div>
520                </div>
521
522
523                
524            </nav>
525
526            <!-- CTA Button -->
527            <div class="hidden items-center gap-4 lg:flex">
528                <form method="POST" action="https://lave.live/language">
529    <input type="hidden" name="_token" value="AjMCTFOftFon9p8sIzSEEUc4qAhUkKFeGD31X2bH" autocomplete="off">    <input type="hidden" name="return_to" value="/online-store">
530    <!--[if BLOCK]><![endif]-->        <label class="relative inline-flex min-h-[44px] items-center gap-2 px-3 py-2 text-sm font-bold text-gray-700 transition-colors hover:text-pink-600 focus-within:ring-2 focus-within:ring-pink-500 focus-within:ring-offset-2">
531            <span aria-hidden="true" uk-icon="icon: world; ratio: 0.8"></span>
532            <span aria-hidden="true">EN</span>
533            <span aria-hidden="true" uk-icon="icon: chevron-down; ratio: 0.8"></span>
534            <span class="sr-only">Language</span>
535            <select name="locale" class="absolute inset-0 h-full w-full cursor-pointer opacity-0"
536                onchange="this.form.requestSubmit()">
537                <!--[if BLOCK]><![endif]-->                    <option value="en" selected>English</option>
538                                    <option value="fr" >Français</option>
539                                    <option value="ar" >العربية</option>
540                <!--[if ENDBLOCK]><![endif]-->            </select>
541        </label>
542    <!--[if ENDBLOCK]><![endif]--></form>
543                <a href="https://lave.live/register" wire:navigate>
544                    <button
545                        class="rounded-lg bg-pink-500 px-5 py-2 text-sm font-bold text-white transition-colors hover:bg-pink-600">
546                        Login / Register
547                    </button>
548                </a>
549            </div>
550        </div>
551    </header>
552</div>
553
554<!-- Mobile Nav -->
555<div x-data="{ mobileMenuOpen: false }" x-effect="document.documentElement.classList.toggle('overflow-hidden', mobileMenuOpen)"
556    x-on:keydown.escape.window="mobileMenuOpen = false" class="sticky top-0 block lg:hidden" style="z-index: 15;">
557    <!-- Offcanvas Sidebar Wrapper (initially hidden) -->
558    <div x-show="mobileMenuOpen" x-cloak x-transition.opacity style="z-index: 20;" class="fixed inset-0 overscroll-contain overflow-y-auto">
559        <!-- Overlay -->
560        <div x-on:click="mobileMenuOpen = false"
561            class="fixed inset-0 bg-black bg-opacity-50 transition-opacity duration-300 overscroll-contain overflow-y-auto"></div>
562
563        <!-- Fullscreen Sidebar -->
564
565        <div x-transition:enter="transition-transform duration-300 ease-in-out"
566            x-transition:enter-start="-translate-x-full" x-transition:enter-end="translate-x-0"
567            x-transition:leave="transition-transform duration-300 ease-in-out"
568            x-transition:leave-start="translate-x-0" x-transition:leave-end="-translate-x-full"
569            class="fixed inset-0 overflow-y-auto bg-white p-6 shadow-lg overscroll-contain" style="z-index: 20;">
570
571            <!-- Brand -->
572            <a href="https://lave.live" wire:navigate class="text-4xl font-bold"
573                style="font-family: 'Nova Mono'; color: #eb487f;">
574                lave
575            </a>
576
577            <!-- Close Button -->
578            <button type="button" x-on:click="mobileMenuOpen = false"
579                class="absolute end-4 top-4 text-2xl text-gray-600 hover:text-black">✕</button>
580
581            <!-- Divider -->
582            <div class="my-6 flex items-center px-6">
583                <div class="flex-grow border-t border-gray-300"></div>
584                <span class="mx-4 text-gray-400">⋯</span>
585                <div class="flex-grow border-t border-gray-300"></div>
586            </div>
587
588
589            <!-- Navigation -->
590            <nav class="mt-0 space-y-10">
591                <ul class="uk-nav-default space-y-2 text-lg" uk-nav>
592                    <!-- Features Dropdown -->
593                    <li class="uk-parent">
594                        <a href="#">
595                            <span class="uk-margin-xsmall-right" uk-icon="icon: settings"></span>
596                            Features
597                            <span uk-nav-parent-icon></span>
598                        </a>
599                        <ul class="uk-nav-sub space-y-2 ps-4 text-[17px] leading-[1.75rem]">
600                            <li><a wire:navigate href="https://lave.live/features">All Features</a></li>
601                            <li><a wire:navigate href="https://lave.live/record-sales">Sales, Expenses &amp; Debts</a></li>
602                            <li><a wire:navigate href="https://lave.live/online-store">Online Store</a></li>
603                            <li><a wire:navigate href="https://lave.live/whatsapp-orders">WhatsApp Orders</a></li>
604                            <li><a wire:navigate href="https://lave.live/payments">Payments</a></li>
605                            <li><a wire:navigate href="https://lave.live/inventory">Products &amp; Inventory</a>
606                            </li>
607                            <li><a wire:navigate href="https://lave.live/marketing">Marketing Tools</a></li>
608                            <li><a wire:navigate href="https://lave.live/plugins">Plugins &amp; Integrations</a></li>
609                            <li class="uk-nav-divider"></li>
610                            <li class="uk-nav-header">More from Lave</li>
611                            <li><a wire:navigate href="https://lave.live/advertise">Advertise Your Brand</a>
612                            </li>
613                            <li><a wire:navigate href="https://lave.live/taskers">Become a Tasker</a></li>
614                            <li><a wire:navigate href="https://lave.live/refer-and-earn">Refer &amp; Earn</a></li>
615                            
616                        </ul>
617                    </li>
618
619                    <!-- Pricing -->
620                    <li>
621                        <a wire:navigate href="https://lave.live/pricing">
622                            <span class="uk-margin-xsmall-right" uk-icon="icon: tag"></span>
623                            Pricing
624                        </a>
625                    </li>
626
627                    <li class="uk-parent">
628                        <a href="#">
629                            <span class="uk-margin-xsmall-right" uk-icon="icon: info"></span>
630                            Resources
631                            <span uk-nav-parent-icon></span>
632                        </a>
633                        <ul class="uk-nav-sub space-y-2 ps-4 text-[17px] leading-[1.75rem]">
634                            <li><a wire:navigate href="https://lave.live/blog">Blog</a></li>
635                            <li><a wire:navigate href="https://lave.live/help">Help</a></li>
636                            <li><a wire:navigate href="https://lave.live/updates"
637                                    class="block hover:text-pink-500">Updates</a>
638                            </li>
639                            <li><a wire:navigate href="https://lave.live/faqs">FAQs</a></li>
640                            <li><a href="https://www.youtube.com/@lavedotlive" target="_blank">Videos</a></li>
641                            <li><a wire:navigate href="https://lave.live/contact">Contact</a></li>
642                            <li><a wire:navigate href="https://lave.live/about">About</a></li>
643                        </ul>
644                    </li>
645
646                    
647
648                    <li class="uk-nav-divider"></li>
649
650                    <!-- Login/Register -->
651                    <li>
652                        <a href="https://lave.live/register" wire:navigate>
653                            <button class="uk-button uk-width-1-1 bg-pink-500 text-white">
654                                Login / Register
655                            </button>
656                        </a>
657                    </li>
658                </ul>
659            </nav>
660
661
662        </div>
663
664    </div>
665
666    <!-- Mobile Navbar -->
667
668    <nav class="flex items-center justify-between bg-white px-4 py-3 shadow-md">
669        <!-- Left: Logo -->
670        <div class="p-1 text-pink-500">
671            <a href="https://lave.live" class="text-2xl font-bold"
672                style="font-family: 'Nova Mono'; color: #eb487f;" wire:navigate>
673                lave
674            </a>
675        </div>
676
677        <div class="flex items-center gap-3">
678            <!-- Register CTA (Mobile Only) -->
679            
680
681            <form method="POST" action="https://lave.live/language">
682    <input type="hidden" name="_token" value="AjMCTFOftFon9p8sIzSEEUc4qAhUkKFeGD31X2bH" autocomplete="off">    <input type="hidden" name="return_to" value="/online-store">
683    <!--[if BLOCK]><![endif]-->        <label class="relative inline-flex min-h-[44px] items-center gap-2 px-3 py-2 text-sm font-bold text-gray-700 transition-colors hover:text-pink-600 focus-within:ring-2 focus-within:ring-pink-500 focus-within:ring-offset-2">
684            <span aria-hidden="true" uk-icon="icon: world; ratio: 0.8"></span>
685            <span aria-hidden="true">EN</span>
686            <span aria-hidden="true" uk-icon="icon: chevron-down; ratio: 0.8"></span>
687            <span class="sr-only">Language</span>
688            <select name="locale" class="absolute inset-0 h-full w-full cursor-pointer opacity-0"
689                onchange="this.form.requestSubmit()">
690                <!--[if BLOCK]><![endif]-->                    <option value="en" selected>English</option>
691                                    <option value="fr" >Français</option>
692                                    <option value="ar" >العربية</option>
693                <!--[if ENDBLOCK]><![endif]-->            </select>
694        </label>
695    <!--[if ENDBLOCK]><![endif]--></form>
696
697            <!-- Hamburger Icon -->
698            <button type="button" x-on:click="mobileMenuOpen = true" class="text-gray-700 lg:hidden">
699                <svg class="h-6 w-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
700                    <path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" />
701                </svg>
702            </button>
703        </div>
704    </nav>
705
706</div>
707
708
709
710    <section class="bg-pink-50 py-20">
711        <div class="uk-container mx-auto max-w-6xl px-6 text-center">
712            <div class="text-3xl font-extrabold leading-tight text-pink-600 sm:text-4xl md:text-5xl lg:text-6xl">Online Store
713            </div>
714            <p class="mx-auto mt-4 max-w-2xl text-xl text-gray-700">
715                Create a professional online store in 2 minutes that helps you sell faster
716            </p>
717
718            <div class="mt-10">
719                <a href="https://lave.live/register" wire:navigate data-landing-register-trigger
720                    class="inline-block rounded-lg bg-pink-600 px-6 py-2 text-lg font-semibold text-white shadow transition hover:bg-pink-700">
721                    Get Started →
722                </a>
723            </div>
724        </div>
725    </section>
726
727
728    <section class="bg-white py-20">
729        <div class="uk-container mx-auto max-w-6xl space-y-28 px-6">
730
731            <!-- Feature 1 -->
732            <div class="grid grid-cols-1 items-center gap-12 md:grid-cols-2">
733                <div>
734                    <img src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/onlinestore/1.jpg" alt="Launch Store"
735                        class="w-full rounded-xl">
736                </div>
737                <div>
738                    <div class="mb-4 text-3xl font-extrabold">Launch Your Online Store in Minutes</div>
739                    <p class="mb-4 text-lg text-gray-600">
740                        Go live in 2 minutes. Just sign up, add products, and share your link. It&#039;s that easy.
741                    </p>
742                </div>
743            </div>
744
745            <!-- Feature 2 -->
746            <div class="grid grid-cols-1 items-center gap-12 md:grid-cols-2">
747                <div class="order-2 md:order-1">
748                    <div class="mb-4 text-3xl font-extrabold">Receive Orders via WhatsApp</div>
749                    <p class="mb-4 text-lg text-gray-600">
750                        Let customers shop on your store and automatically redirect them to WhatsApp with their full order details saving you time.
751                    </p>
752                    <a wire:navigate href="https://lave.live/whatsapp-orders" class="font-semibold text-pink-600 hover:underline">See All Features →</a>
753                </div>
754                <div class="order-1 md:order-2">
755                    <div class="flex justify-center">
756                        <div class="relative" style="width: 450px; height: 450px;">
757                            <div class="video-container">
758                                <video class="absolute inset-0 h-full w-full object-contain"
759                                    src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/index/whatsapp.webm?v=3" muted playsinline loop
760                                    uk-video="autoplay: inview" id="videoElement"></video>
761                                <div class="image-loader" id="videospinner"></div>
762                            </div>
763                        </div>
764                    </div>
765                                        </div>
766            </div>
767
768            <!-- Feature 3 -->
769            <div class="grid grid-cols-1 items-center gap-12 md:grid-cols-2">
770                <div>
771                    <img src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/onlinestore/3.jpg" alt="Sell Anything"
772                        class="w-full rounded-xl">
773                </div>
774                <div>
775                    <div class="mb-4 text-3xl font-extrabold">Sell Faster on Social Media</div>
776                    <p class="mb-4 text-lg text-gray-600">
777                        Add your website link to all your social media profiles and start receiving orders instantly.
778                    </p>
779                </div>
780            </div>
781
782            <!-- Feature 2 -->
783            <div class="grid grid-cols-1 items-center gap-12 md:grid-cols-2">
784                <div class="order-2 md:order-1">
785                    <div class="mb-4 text-3xl font-extrabold">Sell Physical &amp; Digital Products</div>
786                    <p class="mb-4 text-lg text-gray-600">
787                        Whether it&#039;s fashion, food, music, or eBooks, Lave helps you sell both physical and downloadable products with ease.
788                    </p>
789                </div>
790                <div class="order-1 md:order-2">
791                    <img src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/index/sellanything.jpg" alt="WhatsApp Orders"
792                        class="w-full rounded-xl">
793                </div>
794            </div>
795
796
797
798            <div class="grid grid-cols-1 items-center gap-12 md:grid-cols-2">
799                <div>
800                    <img src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/onlinestore/5.jpg" alt="Launch Store"
801                        class="w-full rounded-xl">
802                </div>
803                <div>
804                    <div class="mb-4 text-3xl font-extrabold">Blogs &amp; Pages</div>
805                    <p class="mb-4 text-lg text-gray-600">
806                        Share updates, articles, and stories with your customers through your blog. Create custom pages like About Us, FAQs, or any page your business needs
807                    </p>
808                </div>
809            </div>
810
811        </div>
812    </section>
813
814    <hr>
815
816    <section>
817        <br>
818        <p class="uk-text-bold text-center text-3xl text-pink-600">All the tools you need to sell</p>
819        <div class="uk-container uk-padding">
820            <div class="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
821                <div class="uk-card uk-card-default uk-card-small uk-card-body">
822                    <div class="relative h-10 w-10">
823                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
824                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/sell.jpg?v=1" alt="Website Image">
825                    </div>
826                    <div class="uk-text-bold text-lg">
827                        Sell Any kind of product</div>
828                    <p class="">Whether it&#039;s shoes, eBooks, or even meetings, you can sell any type of product with ease.
829                    </p>
830                </div>
831
832                <div class="uk-card uk-card-default uk-card-body uk-card-small">
833                    <div class="relative h-10 w-10">
834                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
835                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/themes.jpg?v=1" alt="Website Image">
836                    </div>
837                    <div class="uk-text-bold text-lg">Customizeable themes</div>
838                    <p>Change the look and feel of your website anytime. Switch themes, update fonts, and personalize your store to match your brand.</p>
839                </div>
840
841
842                <div class="uk-card uk-card-default uk-card-body uk-card-small">
843                    <div class="relative h-10 w-10">
844                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
845                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/payment_gateway.jpg?v=1"
846                            alt="Website Image">
847                    </div>
848                    <div class="uk-text-bold text-lg">Payments</div>
849                    <p>Enable your store to process payments local &amp; international payments effortlessly.</p>
850                </div>
851
852                <div class="uk-card uk-card-default uk-card-body uk-card-small">
853                    <div class="relative h-10 w-10">
854                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
855                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/inventory_management.jpg?v=1"
856                            alt="Website Image">
857                    </div>
858                    <div class="uk-text-bold text-lg">Inventory Management</div>
859                    <p>Automatically keep track of your stock with built-in inventory management.</p>
860                </div>
861
862                <div class="uk-card uk-card-default uk-card-body uk-card-small">
863                    <div class="relative h-10 w-10">
864                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
865                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/sales_report.jpg?v=1" alt="Website Image">
866                    </div>
867
868                    <div class="uk-text-bold text-lg">Sales Reports</div>
869                    <p>Measure your sales performance with detailed reports, broken down by product, date, and more.</p>
870                </div>
871
872                <div class="uk-card uk-card-default uk-card-body uk-card-small">
873                    <div class="relative h-10 w-10">
874                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
875                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/run_sales.jpg?v=1" alt="Website Image">
876                    </div>
877                    <div class="uk-text-bold text-lg">Run sales</div>
878                    <p>
878Boost your sales by generating coupon and discount codes to attract more customers.</p>
879                </div>
880
881                <div class="uk-card uk-card-default uk-card-body uk-card-small">
882
883                    <div class="relative h-10 w-10">
884                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
885                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/facebook_pixel.jpg?v=1"
886                            alt="Website Image">
887                    </div>
888                    <div class="uk-text-bold text-lg">Advertising &amp; Analytics Tracking</div>
889                    <p>Connect Meta Pixel, TikTok Pixel, Google Tag Manager, and server-side tracking to measure the customer journey more reliably.</p>
890                </div>
891
892                <div class="uk-card uk-card-default uk-card-body uk-card-small">
893                    <div class="relative h-10 w-10">
894                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
895                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/custom_domain.jpg?v=1"
896                            alt="Website Image">
897                    </div>
898                    <div class="uk-text-bold text-lg">Custom Domain</div>
899                    <p>Connect a custom domain to your website for example www.yourbusiness.com</p>
900                </div>
901
902                <div class="uk-card uk-card-default uk-card-body uk-card-small">
903                    <div class="relative h-10 w-10">
904                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
905                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/abandon_cart.jpg?v=1"
906                            alt="Website Image">
907                    </div>
908
909                    <div class="uk-text-bold text-lg">Abandoned-cart Recovery</div>
910                    <p>
910Bring shoppers back with automated reminders when they leave checkout before completing their orders.</p>
911                </div>
912
913                <div class="uk-card uk-card-default uk-card-body uk-card-small">
914                    <div class="relative h-10 w-10">
915                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
916                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/invoice.jpg?v=1" alt="Website Image">
917                    </div>
918                    <div class="uk-text-bold text-lg">Invoices</div>
919                    <p>Create Invoices &amp; share with your customers.</p>
920                </div>
921
922                <div class="uk-card uk-card-default uk-card-body uk-card-small">
923                    <div class="relative h-10 w-10">
924                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
925                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/delivery.jpg?v=1" alt="Website Image">
926                    </div>
927                    <div class="uk-text-bold text-lg">Delivery</div>
928                    <p>Set up delivery locations and prices for your customers.</p>
929                </div>
930                <div class="uk-card uk-card-default uk-card-body uk-card-small">
931                    <div class="relative h-10 w-10">
932                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
933                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/checkout_form.jpg?v=1"
934                            alt="Website Image">
935                    </div>
936
937                    <div class="uk-text-bold text-lg">Checkout form</div>
938                    <p>Add custom questions to your checkout form</p>
939                </div>
940
941
942                <div class="uk-card uk-card-default uk-card-body uk-card-small">
943                    <div class="relative h-10 w-10">
944                        <img class="absolute inset-0 h-full w-full rounded-md object-cover"
945                            src="https://fls-a04cfec4-d445-4c41-8c2c-0bc04adc61a3.laravel.cloud/main_assets/index/features/product_form.jpg?v=1"
946                            alt="Website Image">
947                    </div>
948
949                    <div class="uk-text-bold text-lg"> Product Form</div>
950                    <p>Add custom questions to your product page </p>
951                </div>
952
953
954            </div>
955        </div>
956    </section>
957
958    <hr>
959
960
961    <section class="bg-white py-8">
962        <div class="mx-auto max-w-4xl px-4">
963            <div class="mb-10 text-center text-4xl font-bold text-pink-600">Frequently Asked Questions</div>
964
965            <div class="space-y-4">
966
967                
968
969
970                <!--[if BLOCK]><![endif]-->                    <div class="faq-item overflow-hidden rounded-lg border border-gray-200">
971                        <button
972                            class="faq-toggle flex w-full items-center justify-between bg-gray-50 px-3 py-2 text-start text-base font-medium text-gray-800">
973                            <span>What is Lave?</span>
974                            <span class="plus transform text-xl transition-transform">+</span>
975                        </button>
976                        <div
977                            class="faq-content max-h-0 overflow-hidden px-6 text-gray-600 transition-all duration-300 ease-in-out">
978                            <p class="py-4">Lave is an all-in-one platform that helps African entrepreneurs launch websites, manage orders, accept payments, and promote their businesses — all from one simple dashboard.</p>
979                        </div>
980                    </div>
981                                    <div class="faq-item overflow-hidden rounded-lg border border-gray-200">
982                        <button
983                            class="faq-toggle flex w-full items-center justify-between bg-gray-50 px-3 py-2 text-start text-base font-medium text-gray-800">
984                            <span>Do I need technical skills to use Lave?</span>
985                            <span class="plus transform text-xl transition-transform">+</span>
986                        </button>
987                        <div
988                            class="faq-content max-h-0 overflow-hidden px-6 text-gray-600 transition-all duration-300 ease-in-out">
989                            <p class="py-4">No. Lave is built for non-tech-savvy users. You can set up a store, upload products, and start receiving orders without writing a single line of code.</p>
990                        </div>
991                    </div>
992                                    <div class="faq-item overflow-hidden rounded-lg border border-gray-200">
993                        <button
994                            class="faq-toggle flex w-full items-center justify-between bg-gray-50 px-3 py-2 text-start text-base font-medium text-gray-800">
995                            <span>How long does it take to set up?</span>
996                            <span class="plus transform text-xl transition-transform">+</span>
997                        </button>
998                        <div
999                            class="faq-content max-h-0 overflow-hidden px-6 text-gray-600 transition-all duration-300 ease-in-out">
1000                            <p class="py-4">It usually takes less than 2 minutes to create your store and start selling.</p>
1001                        </div>
1002                    </div>
1003                                    <div class="faq-item overflow-hidden rounded-lg border border-gray-200">
1004                        <button
1005                            class="faq-toggle flex w-full items-center justify-between bg-gray-50 px-3 py-2 text-start text-base font-medium text-gray-800">
1006                            <span>Can I have multiple stores?</span>
1007                            <span class="plus transform text-xl transition-transform">+</span>
1008                        </button>
1009                        <div
1010                            class="faq-content max-h-0 overflow-hidden px-6 text-gray-600 transition-all duration-300 ease-in-out">
1011                            <p class="py-4">Yes. You can manage multiple stores from a single Lave dashboard.</p>
1012                        </div>
1013                    </div>
1014                                    <div class="faq-item overflow-hidden rounded-lg border border-gray-200">
1015                        <button
1016                            class="faq-toggle flex w-full items-center justify-between bg-gray-50 px-3 py-2 text-start text-base font-medium text-gray-800">
1017                            <span>What does the Lave Business plan include?</span>
1018                            <span class="plus transform text-xl transition-transform">+</span>
1019                        </button>
1020                        <div
1021                            class="faq-content max-h-0 overflow-hidden px-6 text-gray-600 transition-all duration-300 ease-in-out">
1022                            <p class="py-4">The Business plan includes a professional online store, payments and invoices, a custom domain, staff access, analytics, and marketing automation. Visit the pricing page to see the current price for your country.</p>
1023                        </div>
1024                    </div>
1025                                    <div class="faq-item overflow-hidden rounded-lg border border-gray-200">
1026                        <button
1027                            class="faq-toggle flex w-full items-center justify-between bg-gray-50 px-3 py-2 text-start text-base font-medium text-gray-800">
1028                            <span>What payment methods are supported?</span>
1029                            <span class="plus transform text-xl transition-transform">+</span>
1030                        </button>
1031                        <div
1032                            class="faq-content max-h-0 overflow-hidden px-6 text-gray-600 transition-all duration-300 ease-in-out">
1033                            <p class="py-4">We support mobile money, card payments, bank transfers, and even cash on delivery — depending on your country.</p>
1034                        </div>
1035                    </div>
1036                                    <div class="faq-item overflow-hidden rounded-lg border border-gray-200">
1037                        <button
1038                            class="faq-toggle flex w-full items-center justify-between bg-gray-50 px-3 py-2 text-start text-base font-medium text-gray-800">
1039                            <span>Can I sell digital products like eBooks or music?</span>
1040                            <span class="plus transform text-xl transition-transform">+</span>
1041                        </button>
1042                        <div
1043                            class="faq-content max-h-0 overflow-hidden px-6 text-gray-600 transition-all duration-300 ease-in-out">
1044                            <p class="py-4">Yes. Lave supports both physical and digital product sales. You can upload files, set prices, and customers will automatically receive download links after payment.</p>
1045                        </div>
1046                    </div>
1047                                    <div class="faq-item overflow-hidden rounded-lg border border-gray-200">
1048                        <button
1049                            class="faq-toggle flex w-full items-center justify-between bg-gray-50 px-3 py-2 text-start text-base font-medium text-gray-800">
1050                            <span>Can I connect my own domain name?</span>
1051                            <span class="plus transform text-xl transition-transform">+</span>
1052                        </button>
1053                        <div
1054                            class="faq-content max-h-0 overflow-hidden px-6 text-gray-600 transition-all duration-300 ease-in-out">
1055                            <p class="py-4">Yes. You can connect your custom domain (e.g., www.yourbrand.com) on any of our paid plans.</p>
1056                        </div>
1057                    </div>
1058                                    <div class="faq-item overflow-hidden rounded-lg border border-gray-200">
1059                        <button
1060                            class="faq-toggle flex w-full items-center justify-between bg-gray-50 px-3 py-2 text-start text-base font-medium text-gray-800">
1061                            <span>Does Lave handle delivery for me?</span>
1062                            <span class="plus transform text-xl transition-transform">+</span>
1063                        </button>
1064                        <div
1065                            class="faq-content max-h-0 overflow-hidden px-6 text-gray-600 transition-all duration-300 ease-in-out">
1066                            <p class="py-4">Lave helps you set delivery options and pricing. You can offer delivery by yourself, through third-party services, or cash-on-delivery.</p>
1067                        </div>
1068                    </div>
1069                <!--[if ENDBLOCK]><![endif]-->
1070            </div>
1071            <div class="mt-6 text-center">
1072                <a wire:navigate href="https://lave.live/faqs" class="text-lg font-semibold text-pink-600 hover:underline">
1073                    View More FAQs
1074                </a>
1075            </div>
1076        </div>
1077    </section>
1078
1079
1080
1081
1082    <style>
1083    [data-sticky-register-cta][x-cloak] {
1084        display: none !important;
1085    }
1086
1087    [data-sticky-register-cta] {
1088        position: fixed;
1089        right: 1rem;
1090        bottom: 1rem;
1091        left: 1rem;
1092        z-index: 40;
1093    }
1094
1095    [data-sticky-register-cta] > a {
1096        padding-top: 0.875rem;
1097        padding-bottom: 0.875rem;
1098    }
1099
1100    @media (min-width: 768px) {
1101        [data-sticky-register-cta] {
1102            display: none !important;
1103        }
1104    }
1105</style>
1106
1107<div
1108    x-cloak
1109    data-sticky-register-cta
1110    x-data="{
1111            heroPassed: false,
1112            footerVisible: false,
1113            heroObserver: null,
1114            footerObserver: null,
1115            init() {
1116                const heroCta = document.querySelector('[data-landing-register-trigger]');
1117                const footer = document.querySelector('footer');
1118
1119                if (! heroCta) {
1120                    return;
1121                }
1122
1123                this.heroObserver = new IntersectionObserver(([entry]) => {
1124                    this.heroPassed = ! entry.isIntersecting && entry.boundingClientRect.bottom < 0;
1125                });
1126                this.heroObserver.observe(heroCta);
1127
1128                if (footer) {
1129                    this.footerObserver = new IntersectionObserver(([entry]) => {
1130                        this.footerVisible = entry.isIntersecting;
1131                    }, { threshold: 0.05 });
1132                    this.footerObserver.observe(footer);
1133                }
1134            },
1135            destroy() {
1136                this.heroObserver?.disconnect();
1137                this.footerObserver?.disconnect();
1138            },
1139    }"
1140    x-show="heroPassed && ! footerVisible"
1141    x-transition:enter="transition duration-200 ease-out"
1142    x-transition:enter-start="translate-y-4 opacity-0"
1143    x-transition:enter-end="translate-y-0 opacity-100"
1144    x-transition:leave="transition duration-150 ease-in"
1145    x-transition:leave-start="translate-y-0 opacity-100"
1146    x-transition:leave-end="translate-y-4 opacity-0"
1147    class="fixed bottom-4 start-4 end-4 z-40 md:bottom-6 md:start-auto md:end-6 md:w-72"
1148>
1149    <a
1150        href="https://lave.live/register"
1151        wire:navigate
1152        class="animate-flash block w-full rounded-xl bg-pink-600 px-6 py-3.5 text-center text-base font-bold text-white shadow-2xl transition hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-pink-500 focus:ring-offset-2"
1153        style="text-decoration: none;"
1154    >
1155        Get Started
1156    </a>
1157</div>
1158    <hr>
1159<footer class="bg-gray-50 px-6 pb-6 pt-12 sm:px-12">
1160    <!-- CTA Section -->
1161
1162    <div class="mx-auto max-w-7xl">
1163        <div class="mb-12 rounded-xl bg-pink-700 px-6 py-12 text-center text-white">
1164            <p class="mb-2 text-3xl font-bold sm:text-4xl">Run and grow your business with Lave</p>
1165            <p class="mb-6 text-lg">Manage your sales, customers, payments, and online store—all from one connected app.</p>
1166            <div class="flex flex-col items-center justify-center gap-4 sm:flex-row">
1167                <a href="https://lave.live/register" wire:navigate
1168                    class="rounded-lg bg-white px-6 py-3 font-semibold text-pink-700 transition hover:bg-gray-100">
1169                    Get Started
1170                </a>
1171                <a wire:navigate href="https://lave.live/download"
1172                    class="rounded-lg border-2 border-white px-6 py-3 font-semibold text-white transition hover:bg-white hover:text-pink-700">
1173                    Download App
1174                </a>
1175            </div>
1176        </div>
1177    </div>
1178
1179
1180    <!-- Main Footer Grid -->
1181    <div class="mx-auto grid max-w-7xl grid-cols-1 gap-8 text-sm sm:grid-cols-2 md:grid-cols-4">
1182        <!-- Brand + Social -->
1183        <div class="pb-4">
1184            <p class="mb-4 text-4xl font-semibold" style="font-family: 'Nova Mono'; color: #eb487f;">lave</p>
1185
1186            <p class="mb-4">The easiest way to create your online store and grow your business across Africa.</p>
1187            <div class="mt-2 flex space-x-4 rtl:space-x-reverse">
1188                <a href="https://instagram.com/laveappofficial" target="_blank" aria-label="Instagram"
1189                    uk-icon="icon: instagram" class="text-xl text-gray-700 hover:text-purple-700"></a>
1190                <a href="https://tiktok.com/@laveappofficial" target="_blank" aria-label="tiktok" uk-icon="icon: tiktok"
1191                    class="text-xl text-gray-700 hover:text-purple-700"></a>
1192                <a href="https://x.com/laveappofficial" target="_blank" aria-label="Twitter" uk-icon="icon: twitter"
1193                    class="text-xl text-gray-700 hover:text-purple-700"></a>
1194                <a href="https://facebook.com/laveappofficial" target="_blank" aria-label="Facebook"
1195                    uk-icon="icon: facebook" class="text-xl text-gray-700 hover:text-purple-700"></a>
1196                <a href="https://www.youtube.com/@lavedotlive" target="_blank" aria-label="YouTube"
1197                    uk-icon="icon: youtube" class="text-xl text-gray-700 hover:text-purple-700"></a>
1198            </div>
1199        </div>
1200
1201        <!-- Company -->
1202        <div>
1203            <div class="mb-4 font-semibold text-gray-900">Company</div>
1204            <ul class="space-y-2">
1205                <li><a wire:navigate href="https://lave.live/about" class="hover:underline">About Us</a></li>
1206                <li><a wire:navigate href="https://lave.live/pricing" class="hover:underline">Pricing</a></li>
1207                <li><a wire:navigate href="https://lave.live/contact" class="hover:underline">Contact</a></li>
1208            </ul>
1209        </div>
1210
1211        <!-- Resources -->
1212
1213        <div>
1214            <div class="mb-4 font-semibold text-gray-900">Resources</div>
1215            <ul class="space-y-2">
1216
1217                <li><a wire:navigate href="https://lave.live/blog" class="hover:underline">Blogs</a></li>
1218                <li><a wire:navigate href="https://lave.live/faqs" class="hover:underline">FAQs</a></li>
1219                <li><a href="https://www.youtube.com/@lavedotlive" target="_blank" class="hover:underline">Videos</a>
1220                </li>
1221            </ul>
1222        </div>
1223
1224        <!-- Legal -->
1225        <div>
1226            <div class="mb-4 font-semibold text-gray-900">Legal</div>
1227            <ul class="space-y-2">
1228                <li><a wire:navigate href="https://lave.live/terms" class="hover:underline">Terms &amp; Conditions</a></li>
1229                <li><a wire:navigate href="https://lave.live/privacy" class="hover:underline">Privacy Policy</a></li>
1230            </ul>
1231        </div>
1232    </div>
1233
1234    <div class="mx-auto mt-8 max-w-7xl">
1235        <hr class="m-0 border-gray-200">
1236        <p class="mb-0 pt-6 text-center text-xs text-gray-500">© 2026 Lave. All rights reserved.</p>
1237    </div>
1238</footer>
1239
1240</div>
1241
1242            <div x-persist="website-assistant">
1243            <div>
1244                <div wire:key="lw-3437951620-0-2" wire:snapshot="{&quot;data&quot;:{&quot;question&quot;:&quot;&quot;,&quot;visitorToken&quot;:&quot;onP10QcvwAYZucbZh6U7RrlehpQW6BfQUK3sy1mG&quot;,&quot;conversationId&quot;:null,&quot;failure&quot;:&quot;&quot;},&quot;memo&quot;:{&quot;id&quot;:&quot;C7epeE8TcMSOvzbukEl5&quot;,&quot;name&quot;:&quot;website-assistant&quot;,&quot;path&quot;:&quot;online-store&quot;,&quot;method&quot;:&quot;GET&quot;,&quot;release&quot;:&quot;a-a-a&quot;,&quot;children&quot;:[],&quot;scripts&quot;:[&quot;34428343-0&quot;],&quot;assets&quot;:[],&quot;errors&quot;:[],&quot;locale&quot;:&quot;en&quot;,&quot;islands&quot;:[]},&quot;checksum&quot;:&quot;411d92db85d8c59f9ac5234e0e7c6699d3e077042b83bbdebee2fa8eeda00516&quot;}" wire:effects="{&quot;scripts&quot;:{&quot;34428343-0&quot;:&quot;&lt;script&gt;\n    Alpine.data(&#039;websiteAssistantChat&#039;, () =&gt; ({\n        chatModalOpen: false,\n        sending: false,\n        stopped: false,\n        cancelReply: null,\n        unsubscribe: null,\n        messageObserver: null,\n        destroyed: false,\n        init() {\n            let visible = false;\n            this.messageObserver = new ResizeObserver(([entry]) =&gt; {\n                const nowVisible = entry.contentRect.height &gt; 0 &amp;&amp; entry.contentRect.width &gt; 0;\n                if (nowVisible &amp;&amp; !visible) this.scrollToLatest();\n                visible = nowVisible;\n            });\n            this.messageObserver.observe(this.$refs.messages);\n            this.unsubscribe = this.$wire.intercept(&#039;send&#039;, ({ action, onFinish }) =&gt; {\n                this.stopped = false;\n                this.sending = true;\n                this.scrollToLatest();\n                this.cancelReply = () =&gt; action.cancel();\n                onFinish(() =&gt; {\n                    this.sending = false;\n                    this.cancelReply = null;\n                });\n            });\n        },\n        scrollToQuestion(pending = false) {\n            this.$nextTick(() =&gt; {\n                requestAnimationFrame(() =&gt; {\n                    if (this.destroyed) return;\n                    const container = this.$refs.messages;\n                    const question = [...container.querySelectorAll(&#039;[data-message-role=\&quot;user\&quot;]&#039;)].at(-1);\n                    if (!question) return;\n                    const padding = parseFloat(getComputedStyle(container).paddingTop) || 0;\n                    const top = container.scrollTop + question.getBoundingClientRect().top - container.getBoundingClientRect().top - padding;\n                    container.scrollTo({ top: Math.max(0, top), behavior: &#039;instant&#039; });\n                });\n            });\n        },\n        scrollToLatest() {\n            this.$nextTick(() =&gt; {\n                requestAnimationFrame(() =&gt; {\n                    if (!this.destroyed) this.$refs.messages.scrollTo({ top: this.$refs.messages.scrollHeight, behavior: &#039;instant&#039; });\n                });\n            });\n        },\n        stopReply() {\n            if (!this.sending || !this.cancelReply) return;\n            this.stopped = true;\n            this.cancelReply();\n        },\n        destroy() {\n            this.destroyed = true;\n            this.cancelReply?.();\n            this.unsubscribe?.();\n            this.messageObserver?.disconnect();\n        },\n    }));\n&lt;\/script&gt;\n    &quot;}}" wire:id="C7epeE8TcMSOvzbukEl5" wire:name="website-assistant" x-data="websiteAssistantChat" x-on:assistant-support-opened="chatModalOpen = false" x-on:keydown.escape.window="chatModalOpen = false" x-on:website-assistant-answered.window="scrollToQuestion()">
1245    <button type="button" class="fixed bottom-[88px] end-4 z-10 flex h-14 w-14 cursor-pointer items-center justify-center overflow-hidden rounded-full border-0 bg-pink-500 p-0 text-white shadow-lg hover:bg-pink-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-pink-700" x-ref="launcher" aria-label="Need help? Open Lave AI Assistant" x-bind:aria-expanded="chatModalOpen" x-on:click="chatModalOpen = !chatModalOpen; if(chatModalOpen) $nextTick(() => $refs.question.focus())"><span uk-icon="icon: comment; ratio: 1.5" aria-hidden="true"></span></button>
1246    <section class="fixed bottom-[156px] end-4 z-40 flex h-[min(590px,calc(100dvh-165px))] min-h-[280px] w-96 max-w-[calc(100vw-24px)] flex-col overflow-hidden rounded-2xl border border-gray-200 bg-white text-sm text-gray-700 shadow-xl max-md:inset-0 max-md:z-[1000] max-md:h-dvh max-md:min-h-0 max-md:w-full max-md:max-w-none max-md:rounded-none max-md:border-0 [&_button:disabled]:cursor-wait [&_button:disabled]:opacity-50 [&_button:focus-visible]:outline [&_button:focus-visible]:outline-2 [&_button:focus-visible]:outline-offset-2 [&_button:focus-visible]:outline-pink-700" role="dialog" aria-label="Lave AI Assistant" x-show="chatModalOpen" x-cloak>
1247        <header class="flex shrink-0 items-center justify-between gap-2 border-b border-gray-200 bg-white px-4 py-3 max-md:pt-[max(0.75rem,env(safe-area-inset-top))]">
1248            <div class="flex min-w-0 items-center gap-2"><span class="shrink-0 text-xl text-pink-700" aria-hidden="true">✦</span><div class="min-w-0"><strong class="block text-sm text-gray-800">Lave AI Assistant</strong><small class="block text-xs text-gray-500">A little help getting started</small></div></div>
1249            <button type="button" class="shrink-0 rounded px-2 text-2xl text-gray-600 hover:bg-gray-100" aria-label="Close assistant" x-on:click="chatModalOpen = false;
1249 $refs.launcher.focus()">×</button>
1250        </header>
1251        <div class="flex shrink-0 items-center justify-between gap-3 border-b border-gray-100 bg-gray-100 px-4 py-2.5">
1252            <button type="button" class="inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-gray-600 bg-gray-600 px-3 py-2 text-xs font-medium text-white transition hover:border-gray-700 hover:bg-gray-700" wire:click="startAgain" x-on:click="stopped = false" x-bind:disabled="sending" wire:loading.attr="disabled" wire:target="send,startAgain"><span aria-hidden="true" class="text-base leading-none">+</span> New chat</button>
1253            <button type="button"
1254    class="inline-flex shrink-0 items-center gap-1 rounded text-xs font-bold !no-underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 px-1 py-2 !text-gray-800 hover:text-gray-950 focus-visible:outline-gray-600"
1255    x-on:click="if (typeof window.openZohoChat === 'function') { window.openZohoChat($event); $dispatch('assistant-support-opened'); }"
1256>
1257    <span>Talk to a human</span>
1258</button>
1259        </div>
1260        <div class="min-h-0 min-w-0 flex-1 overflow-auto overscroll-contain px-5 py-4" x-ref="messages" wire:navigate:scroll aria-live="polite" role="log">
1261            <!--[if BLOCK]><![endif]-->                <div class="flex flex-col gap-2.5">
1262                    <h2 class="my-3 text-xl text-gray-700">What do you need help with?</h2>
1263                    <p class="leading-relaxed text-gray-500">Type your question below.</p>
1264                </div>
1265            <!--[if ENDBLOCK]><![endif]-->            <!--[if BLOCK]><![endif]--><!--[if ENDBLOCK]><![endif]-->            <p x-show="stopped" x-cloak class="text-xs text-gray-500" role="status">Reply stopped.</p>
1266            <p x-show="sending" x-cloak class="inline-flex items-center gap-2 text-xs text-pink-700" role="status"><span class="shrink-0" uk-spinner="ratio: 0.45" aria-hidden="true"></span><span>Checking that for you…</span></p>
1267        </div>
1268        <form wire:submit="send" class="shrink-0 border-t border-gray-100 bg-white p-3 max-md:pb-[max(0.75rem,env(safe-area-inset-bottom))]">
1269            <label class="sr-only" for="website-ai-question">Your question</label>
1270            <div class="flex items-end gap-[10px] border border-[#e5dbe9] bg-gray-50 p-[10px] focus-within:border-[#cc79a7]"><textarea class="min-h-0 min-w-0 w-full resize-none border-0 bg-transparent p-[3px] text-sm leading-[1.6] text-[#423448] outline-none focus:ring-0 max-md:text-base" id="website-ai-question" x-ref="question" wire:model="question" x-bind:readonly="sending" rows="2" maxlength="1000" placeholder="Ask about Lave…" required></textarea><button x-show="!sending" type="submit" class="min-h-[40px] shrink-0 rounded-[10px] border-0 bg-[#bd3c80] px-[15px] py-[10px] text-xs text-white hover:bg-pink-700" wire:loading.attr="disabled" wire:target="send,startAgain"><span wire:loading wire:target="send" uk-spinner="ratio: 0.4"></span>Send</button><button type="button" x-show="sending" x-cloak x-on:click="stopReply" class="min-h-[40px] shrink-0 rounded-[10px] border-0 bg-[#bd3c80] px-[15px] py-[10px] text-xs text-white hover:bg-pink-700" aria-label="Stop generating reply">Stop</button></div>
1271            <!--[if BLOCK]><![endif]--><!--[if ENDBLOCK]><![endif]-->            <!--[if BLOCK]><![endif]--><!--[if ENDBLOCK]><![endif]-->            <small class="mt-2 block text-[10px] leading-normal text-gray-500">AI answers can be mistaken. Chats are saved for continuity.</small>
1272        </form>
1273    </section>
1274</div>
1275
1276
1277
1278                </div>
1279        </div>
1280        
1281
1282    
1282<script>
1283        (() => {
1284            if (window.laveCanPullToRefresh) return;
1285
1286            let gestureBlocked = false;
1287
1288            function isVisible(element) {
1289                if (element.closest('[hidden], [inert], [aria-hidden="true"], [x-cloak]')) return false;
1290
1291                const style = getComputedStyle(element);
1292                const rect = element.getBoundingClientRect();
1293
1294                return style.display !== 'none' && style.visibility !== 'hidden'
1295                    && rect.width > 0 && rect.height > 0
1296                    && rect.bottom > 0 && rect.right > 0
1297                    && rect.top < innerHeight && rect.left < innerWidth;
1298            }
1299
1300            function hasOpenOverlay() {
1301                if (typeof window.isPageScrollLocked === 'function' && window.isPageScrollLocked()) return true;
1302
1303                return Array.from(document.querySelectorAll(
1304                    '[data-pull-refresh-overlay], [role="dialog"], [aria-modal="true"], dialog[open], '
1305                    + '.uk-modal.uk-open, .uk-offcanvas.uk-open, .fixed.inset-0'
1306                )).some(isVisible);
1307            }
1308
1309            function startsInScrollArea(target) {
1310                for (let element = target; element instanceof Element && element !== document.body
1311                    && element !== document.documentElement; element = element.parentElement) {
1312                    const style = getComputedStyle(element);
1313
1314                    // Contained areas keep their gestures even at the top or with short content.
1315                    if (['contain', 'none'].includes(style.overscrollBehaviorY)
1316                        || (/auto|scroll/.test(style.overflowY) && element.scrollHeight > element.clientHeight)) {
1317                        return true;
1318                    }
1319                }
1320
1321                return false;
1322            }
1323
1324            document.addEventListener('touchstart', event => {
1325                gestureBlocked = event.touches.length !== 1 || hasOpenOverlay()
1326                    || startsInScrollArea(event.target);
1327            }, { capture: true, passive: true });
1328
1329            // Keep the decision through touchend and any delayed refresh callback.
1330            document.addEventListener('touchcancel', () => { gestureBlocked = true; }, { passive: true });
1331            document.addEventListener('livewire:navigating', () => { gestureBlocked = true; });
1332
1333            window.laveCanPullToRefresh = () => !gestureBlocked && !window.scrollY && !hasOpenOverlay();
1334        })();
1335
1336        function initPullToRefresh() {
1337            if (!window.PullToRefresh || typeof window.PullToRefresh.init !== 'function') {
1338                return;
1339            }
1340
1341            if (window.__lavePullToRefresh) {
1342                window.__lavePullToRefresh.destroy();
1343                window.__lavePullToRefresh = null;
1344            }
1345
1346            const pullToRefreshDisabledPaths = [
1347                '/dashboard/store/products/bulk-upload',
1348                '/dashboard/store/products/bulk-upload/csv',
1349                '/dashboard/store/products/bulk-edit',
1350            ];
1351            const currentPath = window.location.pathname.replace(/\/$/, '');
1352
1353            if (currentPath.includes('/sort') || pullToRefreshDisabledPaths.includes(currentPath)) {
1354                return;
1355            }
1356
1357            window.__lavePullToRefresh = window.PullToRefresh.init({
1358                mainElement: 'body',
1359                shouldPullToRefresh: function() {
1360
1361                    return window.laveCanPullToRefresh();
1362                },
1363                onRefresh: function() {
1364                    if (window.laveCanPullToRefresh()) window.location.reload(true);
1365                }
1366            });
1367        }
1368
1369        if (document.readyState === 'loading') {
1370            document.addEventListener('DOMContentLoaded', initPullToRefresh, {
1371                once: true
1372            });
1373        } else {
1374            initPullToRefresh();
1375        }
1376
1377        document.addEventListener('livewire:navigated', initPullToRefresh);
1378    </script>
1378
1379
1380
1381
1382    
1382<script>
1383        function goback() {
1384            if (window.history.length > 1) {
1385                window.history.back();
1386            } else {
1387                window.location.href = "/home/admin";
1388            }
1389        }
1390    </script>
1390
1391
1392
1393
1394    <!-- ALL Faq script -->
1395    
1395<script>
1396        function bindFaqToggleEvents() {
1397            const toggles = document.querySelectorAll('.faq-toggle');
1398
1399            toggles.forEach(toggle => {
1400                if (!toggle.dataset.bound) {
1401                    toggle.addEventListener('click', () => {
1402                        const item = toggle.closest('.faq-item');
1403                        const content = item.querySelector('.faq-content');
1404                        const icon = toggle.querySelector('.plus');
1405
1406                        // Collapse other items
1407                        document.querySelectorAll('.faq-content').forEach(c => {
1408                            if (c !== content) {
1409                                c.style.maxHeight = null;
1410                                c.parentElement.classList.remove('active');
1411                                c.previousElementSibling.querySelector('.plus').textContent = '+';
1412                            }
1413                        });
1414
1415                        // Toggle current
1416                        if (content.style.maxHeight) {
1417                            content.style.maxHeight = null;
1418                            icon.textContent = '+';
1419                        } else {
1420                            content.style.maxHeight = content.scrollHeight + "px";
1421                            icon.textContent = '−';
1422                        }
1423                    });
1424
1425                    toggle.dataset.bound = "true"; // Prevent duplicate bindings
1426                }
1427            });
1428        }
1429
1430        // Initial bind
1431        document.addEventListener('DOMContentLoaded', bindFaqToggleEvents);
1432        document.addEventListener('livewire:navigated', bindFaqToggleEvents);
1433    </script>
1433
1434
1435
1436
1437    
1437<script>
1438        if ('serviceWorker' in navigator) {
1439            let refreshing = false;
1440            let lastUpdateCheckAt = 0;
1441            const minUpdateIntervalMs = 30 * 60 * 1000; // 30 minutes
1442
1443            const maybeUpdate = (reg, reason) => {
1444                const now = Date.now();
1445
1446                if (document.hidden) {
1447                    return;
1448                }
1449
1450                if (now - lastUpdateCheckAt < minUpdateIntervalMs) {
1451                    return;
1452                }
1453
1454                lastUpdateCheckAt = now;
1455
1456                if (reason) {
1457                    console.log(`[SW] ${reason} - checking for updates...`);
1458                }
1459
1460                reg.update();
1461            };
1462
1463            // Register service worker
1464            navigator.serviceWorker.register('/serviceworker.js', {
1465                    updateViaCache: 'none' // Always check for updates, never use cached service worker
1466                })
1467                .then(reg => {
1468                    console.log('[SW] Service worker registered:', reg.scope);
1469
1470                    // IMMEDIATE: Check for updates on every page load
1471                    maybeUpdate(reg, 'App load');
1472
1473                    // Check for updates when page becomes visible (app opened)
1474                    document.addEventListener('visibilitychange', () => {
1475                        if (!document.hidden) {
1476                            if (Date.now() - lastUpdateCheckAt < minUpdateIntervalMs) {
1477                                return;
1478                            }
1479
1480                            lastUpdateCheckAt = Date.now();
1481                            console.log('[SW] App visible - checking for updates...');
1482                            reg.update();
1483                        }
1484                    });
1485
1486                    // Check for updates when app comes to foreground (PWA)
1487                    window.addEventListener('focus', () => {
1488                        if (Date.now() - lastUpdateCheckAt < minUpdateIntervalMs) {
1489                            return;
1490                        }
1491
1492                        lastUpdateCheckAt = Date.now();
1493                        console.log('[SW] App focused - checking for updates...');
1494                        reg.update();
1495                    });
1496
1497                    // Check for updates every 24 hours (background safety check)
1498                    // Most updates are caught by: app load, visibility change, and focus events above
1499                    setInterval(() => {
1500                        maybeUpdate(reg, 'Background check');
1501                    }, 24 * 60 * 60 * 1000); // 24 hours
1502
1503                    // Listen for new service worker found
1504                    reg.addEventListener('updatefound', () => {
1505                        const newWorker = reg.installing;
1506                        if (newWorker) {
1507                            console.log('[SW] New service worker version detected!');
1508                            newWorker.addEventListener('statechange', () => {
1509                                if (newWorker.state === 'installed') {
1510                                    if (navigator.serviceWorker.controller) {
1511                                        // New service worker is ready
1512                                        console.log('[SW] New version installed, activating...');
1513                                        // Service worker will auto-activate due to skipWaiting()
1514                                    } else {
1515                                        // First time installation
1516                                        console.log('[SW] Service worker installed for first time');
1517                                    }
1518                                }
1519                            });
1520                        }
1521                    });
1522                })
1523                .catch(err => console.error('[SW] Registration failed:', err));
1524
1525            // Also check for updates if service worker is already registered (for returning users)
1526            window.addEventListener('load', () => {
1527                if (Date.now() - lastUpdateCheckAt < minUpdateIntervalMs) {
1528                    return;
1529                }
1530
1531                lastUpdateCheckAt = Date.now();
1532                navigator.serviceWorker.getRegistration().then(reg => {
1533                    if (reg) {
1534                        console.log('[SW] Checking for updates on app load...');
1535                        reg.update();
1536                    }
1537                });
1538            });
1539
1540            // Handle controller change (new service worker activated)
1541            navigator.serviceWorker.addEventListener('controllerchange', () => {
1542                if (!refreshing) {
1543                    refreshing = true;
1544                    console.log('[SW] New version activated - reloading page...');
1545                    window.location.reload();
1546                }
1547            });
1548        }
1549    </script>
1549
1550
1551
1552
1553    <style>
1554    body {
1555        /* Keep horizontal safe-area support without creating a visible top gap */
1556        padding-top: 0;
1557        padding-bottom: 0;
1558        padding-left: env(safe-area-inset-left);
1559        padding-right: env(safe-area-inset-right);
1560    }
1561
1562
1563    .safe-area {
1564        padding-bottom: max(15px, env(safe-area-inset-bottom));
1565        z-index: 10;
1566    }
1567</style>
1568
1569
1570    <div>
1571
1572    
1573    <div id="contactPickerModal" class="fixed inset-0 hidden items-center justify-center bg-gray-900 bg-opacity-50 px-4"
1574        style="z-index: 60;">
1575        <div class="relative w-full max-w-sm overflow-hidden rounded-2xl bg-white shadow-2xl">
1576            <!-- Header with X button -->
1577            <div class="flex items-center justify-between border-b border-gray-200 px-5 py-4">
1578                <div id="contactNameHeader" class="truncate text-lg font-semibold text-gray-900">
1579                    Select Phone Number
1580                </div>
1581                <button id="closeModalPhone" type="button"
1582                    class="flex h-8 w-8 items-center justify-center rounded-full text-gray-400 transition hover:bg-gray-100 hover:text-gray-600">
1583                    <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1584                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
1585                            d="M6 18L18 6M6 6l12 12" />
1586                    </svg>
1587                </button>
1588            </div>
1589
1590            <!-- Contact list with fixed max height -->
1591            <div id="contactList" class="max-h-96 divide-y divide-gray-100 overflow-y-auto"></div>
1592        </div>
1593    </div>
1594
1595    
1596    
1596<script data-navigate-once>
1597        /**
1598         * Initialize custom headers for all HTTP requests
1599         * This allows your Laravel server to detect if the request is from the Capacitor app
1600         */
1601        async function initAppHeaders() {
1602            if (!window.Capacitor) {
1603                console.log('? Capacitor not detected - running in web browser');
1604                return;
1605            }
1606
1607            console.log('🔵 Capacitor detected! Initializing app headers...');
1608
1609            try {
1610                const {
1611                    Device,
1612                    App
1613                } = window.Capacitor.Plugins;
1614
1615                // Get app and device info
1616                const appInfo = await App.getInfo();
1617                const deviceInfo = await Device.getInfo();
1618
1619                // Store app info globally
1620                window._appHeaders = {
1621                    'X-Capacitor-App': 'true',
1622                    'X-App-Version': appInfo.version,
1623                    'X-App-Build': appInfo.build,
1624                    'X-App-Platform': deviceInfo.platform, // 'ios' or 'android'
1625                    'X-Device-Model': deviceInfo.model,
1626                };
1627
1628                console.log('? App headers initialized:', window._appHeaders);
1629
1630                // Intercept fetch requests to add headers
1631                const originalFetch = window.fetch;
1632                window.fetch = function(...args) {
1633                    const [url, config = {}] = args;
1634
1635                    // Only add headers to requests to your domain
1636                    if (typeof url === 'string' && (url.includes('lave.live') || url.startsWith('/'))) {
1637                        config.headers = {
1638                            ...config.headers,
1639                            ...window._appHeaders
1640                        };
1641                    }
1642
1643                    return originalFetch(url, config);
1644                };
1645
1646                // Intercept XMLHttpRequest (for HTMX and other libraries)
1647                const originalOpen = XMLHttpRequest.prototype.open;
1648                const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
1649
1650                XMLHttpRequest.prototype.open = function(...args) {
1651                    this._url = args[1];
1652                    return originalOpen.apply(this, args);
1653                };
1654
1655                XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
1656                    // Store original headers
1657                    if (!this._customHeaders) {
1658                        this._customHeaders = {};
1659                    }
1660                    this._customHeaders[header] = value;
1661                    return originalSetRequestHeader.apply(this, arguments);
1662                };
1663
1664                // Add app headers before send
1665                const originalSend = XMLHttpRequest.prototype.send;
1666                XMLHttpRequest.prototype.send = function(...args) {
1667                    // Only add headers to requests to your domain
1668                    if (this._url && (typeof this._url === 'string' && (this._url.includes('lave.live') || this._url
1669                            .startsWith('/')))) {
1670                        for (const [header, value] of Object.entries(window._appHeaders)) {
1671                            originalSetRequestHeader.call(this, header, value);
1672                        }
1673                    }
1674                    return originalSend.apply(this, args);
1675                };
1676
1677                console.log('? Request interceptors initialized - All requests will include app headers');
1678
1679            } catch (error) {
1680                console.error('? Failed to initialize app headers:', error);
1681                alert('? Error initializing app headers: ' + error.message);
1682            }
1683        }
1684
1685        // Initialize as early as possible
1686        if (document.readyState === 'loading') {
1687            document.addEventListener('DOMContentLoaded', initAppHeaders);
1688        } else {
1689            initAppHeaders();
1690        }
1691    </script>
1691
1692
1693
1694
1695
1696    
1697    
1697<script data-navigate-once>
1698        /**
1699         * Lock text zoom to 1.0 (100%) to prevent system font size from affecting the app
1700         * This ensures your app always uses your default font size
1701         */
1702        async function lockTextZoom() {
1703            // Wait for Capacitor to be available
1704            if (!window.Capacitor) {
1705                setTimeout(lockTextZoom, 100);
1706                return;
1707            }
1708
1709            // Wait for TextZoom plugin to be available
1710            if (!window.Capacitor.Plugins || !window.Capacitor.Plugins.TextZoom) {
1711                setTimeout(lockTextZoom, 100);
1712                return;
1713            }
1714
1715            try {
1716                // Set text zoom to 1.0 (100% - normal size)
1717                await window.Capacitor.Plugins.TextZoom.set({
1718                    value: 1.0
1719                });
1720                console.log('? Text zoom locked to 1.0 (100%)');
1721            } catch (error) {
1722                console.log('Could not set text zoom:', error);
1723            }
1724        }
1725
1726        // Lock text zoom when DOM is ready
1727        if (document.readyState === 'loading') {
1728            document.addEventListener('DOMContentLoaded', lockTextZoom);
1729        } else {
1730            // DOM is already ready
1731            lockTextZoom();
1732        }
1733
1734        // Also lock after window loads (as a fallback)
1735        window.addEventListener('load', lockTextZoom);
1736
1737        // Re-lock text zoom after HTMX content swaps
1738        document.body.addEventListener("htmx:afterSwap", lockTextZoom);
1739    </script>
1739
1740
1741    
1741<script data-navigate-once>
1742        async function getAppInfo() {
1743            if (!window.Capacitor || !window.Capacitor.Plugins?.App) {
1744                //  alert("Capacitor App plugin not available.");
1745                return;
1746            }
1747
1748            const App = window.Capacitor.Plugins.App;
1749
1750            try {
1751                const info = await App.getInfo();
1752                const state = await App.getState();
1753
1754                // alert(
1755                //     "App Info:\n" +
1756                //     "Name: " + info.name + "\n" +
1757                //     "ID: " + info.id + "\n" +
1758                //     "Version: " + info.version + "\n" +
1759                //     "Build: " + info.build + "\n" +
1760                //     "State: " + state.isActive
1761                // );
1762
1763                console.log("Full App Info:", info);
1764                console.log("App State:", state);
1765
1766            } catch (err) {
1767                console.error("Error getting app info:", err);
1768            }
1769        }
1770
1771        document.addEventListener("deviceready", getAppInfo);
1772        document.addEventListener("DOMContentLoaded", getAppInfo);
1773        document.addEventListener('livewire:navigated', getAppInfo);
1774    </script>
1774
1775
1776    
1776<script data-navigate-once>
1777        // Capture app launch time for grace windows
1778        const appLaunchTime = Date.now();
1779
1780        function initCapacitorCore() {
1781            if (!window.Capacitor) return;
1782
1783            // Back Button Handler - Simple version using Capacitor's built-in canGoBack
1784            if (!window._backHandlerSet) {
1785                window._backHandlerSet = true;
1786                const {
1787                    App
1788                } = window.Capacitor.Plugins;
1789
1790                // Handle back button
1791                App.addListener('backButton', async (event) => {
1792                    console.log('📱 Back button pressed. canGoBack:', event.canGoBack);
1793
1794                    if (event.canGoBack) {
1795                        // Navigate back in web history
1796                        window.history.back();
1797                        console.log('⬅️ Navigating back in history');
1798                    } else {
1799                        // No history, minimize the app (move to background)
1800                        console.log('📱 No more history, minimizing app...');
1801                        try {
1802                            await App.minimizeApp();
1803                            console.log('? App minimized');
1804                        } catch (error) {
1805                            console.error('? Failed to minimize app:', error);
1806                        }
1807                    }
1808                });
1809
1810                console.log('? Back button handler initialized');
1811            }
1812
1813            // Share Override - intercepts navigator.share() calls to use Capacitor native share
1814            if (!navigator._shareOverrideSet) {
1815                navigator._shareOverrideSet = true;
1816                navigator.share = async (data) => {
1817                    return await window.Capacitor.Plugins.Share.share(data);
1818                };
1819            }
1820
1821            // Network Monitoring - Simple and reliable
1822            if (!window._networkMonitorSet) {
1823                window._networkMonitorSet = true;
1824
1825                async function saveUrlAndGoOffline() {
1826                    // Grace period after launch to avoid false offline on cold start
1827                    const sinceLaunch = Date.now() - appLaunchTime;
1828                    if (sinceLaunch < 4000) {
1829                        console.warn('Offline redirect suppressed (grace window)', sinceLaunch);
1830                        return;
1831                    }
1832
1833                    // If browser reports online, skip redirect
1834                    if (navigator.onLine) {
1835                        console.warn('Offline redirect skipped because navigator.onLine');
1836                        return;
1837                    }
1838
1839                    const currentUrl = window.location.href;
1840
1841                    // Save URL using Capacitor Preferences
1842                    try {
1843                        if (window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.Preferences) {
1844                            await window.Capacitor.Plugins.Preferences.set({
1845                                key: 'returnUrl',
1846                                value: currentUrl
1847                            });
1848                        } else {
1849                            localStorage.setItem('capacitor_returnUrl', currentUrl);
1850                        }
1851                    } catch (e) {
1852                        try {
1853                            localStorage.setItem('capacitor_returnUrl', currentUrl);
1854                        } catch (e2) {}
1855                    }
1856
1857                    // Navigate to offline page
1858                    window.location.href = 'offline.html';
1859                }
1860
1861                // Livewire requests use fetch instead of HTMX's request events.
1862                let livewireNetworkMonitorSet = false;
1863                function monitorLivewireRequests() {
1864                    if (livewireNetworkMonitorSet || !window.Livewire?.interceptRequest) return;
1865                    livewireNetworkMonitorSet = true;
1866                    window.Livewire.interceptRequest(({ onFailure }) => {
1867                        onFailure(() => saveUrlAndGoOffline());
1868                    });
1869                }
1870                document.addEventListener('livewire:initialized', monitorLivewireRequests);
1871                document.addEventListener('livewire:navigated', monitorLivewireRequests);
1872                monitorLivewireRequests();
1873
1874                // HTMX: Request failed to send (offline)
1875                document.body.addEventListener('htmx:sendError', function() {
1876                    console.log('? HTMX sendError - going offline');
1877                    saveUrlAndGoOffline();
1878                });
1879
1880                // HTMX: Request sent but got network error (status 0)
1881                document.body.addEventListener('htmx:responseError', function(event) {
1882                    if (event.detail.xhr.status === 0) {
1883                        console.log('? HTMX responseError status 0 - going offline');
1884                        saveUrlAndGoOffline();
1885                    }
1886                });
1887
1888                console.log('? Network monitoring ready');
1889            }
1890        }
1891        document.addEventListener("DOMContentLoaded", initCapacitorCore);
1892        document.addEventListener('livewire:navigated', initCapacitorCore);
1893        document.body.addEventListener("htmx:afterSwap", initCapacitorCore);
1894    </script>
1894
1895
1896    
1897    <!-- Add this to your initOneSignal() function -->
1898
1899    
1899<script data-navigate-once>
1900        // Avoid double init on htmx reloads
1901        let oneSignalInitStarted = false;
1902        let oneSignalReady = false;
1903
1904        // Store pending deep links to apply after app is ready
1905        const DEEPLINK_KEY = 'pending_deeplink';
1906
1907        function handleOneSignalOpen(notification) {
1908            if (!notification) {
1909                console.log("OneSignal: click with no payload");
1910                return;
1911            }
1912
1913            const additionalData = notification.additionalData || {};
1914            const target =
1915                additionalData.targetUrl ||
1916                additionalData.url ||
1917                notification.launchURL;
1918
1919            console.log("OneSignal payload", {
1920                additionalData,
1921                launchURL: notification.launchURL,
1922                resolvedTarget: target
1923            });
1924
1925            if (!target) {
1926                console.log("OneSignal: no target URL found");
1927                return;
1928            }
1929
1930            // Stash target to apply after app is fully ready
1931            try {
1932                localStorage.setItem(DEEPLINK_KEY, target);
1933                console.log("Stored pending deep link", target);
1934            } catch (e) {
1935                console.warn("Could not store deep link", e);
1936            }
1937
1938            // Try to hide splash immediately to avoid freeze
1939            if (window.Capacitor?.Plugins?.SplashScreen) {
1940                window.Capacitor.Plugins.SplashScreen.hide().catch(() => {});
1941            }
1942        }
1943
1944        async function initOneSignal() {
1945            if (!window.plugins?.OneSignal) return;
1946            if (oneSignalInitStarted) {
1947                // Navigation can change the signed-in user or follow a failed save.
1948                if (oneSignalReady) await syncOneSignalSubscription();
1949                return;
1950            }
1951            oneSignalInitStarted = true;
1952
1953            try {
1954                // Enable debug logs (optional for dev, turn down in production)
1955                window.plugins.OneSignal.Debug.setLogLevel(6);
1956
1957                // Initialize with your OneSignal App ID
1958                window.plugins.OneSignal.initialize("38da2164-7e3c-4830-a4bc-8a975f1cee53");
1959
1960                // Request notification permission if needed
1961                const hasPermission = await window.plugins.OneSignal.Notifications.hasPermission();
1962                if (!hasPermission) {
1963                    const accepted = await window.plugins.OneSignal.Notifications.requestPermission(true);
1964                    if (!accepted) {
1965                        console.log('🔔 Notification permission denied');
1966                        alert(
1967                            '🔔 Notifications disabled\n\nTo receive updates and messages, enable notifications:\n\nSettings > Apps > Lave > Notifications'
1968                        );
1969                        oneSignalInitStarted = false;
1970                        return;
1971                    }
1972                }
1973
1974                // Ensure subscription is opted in
1975                await window.plugins.OneSignal.User.pushSubscription.optIn();
1976
1977                // 🔥 Handle notification opened events
1978                window.plugins.OneSignal.Notifications.addEventListener("click", (event) => {
1979                    console.log("Notification clicked:", event);
1980                    handleOneSignalOpen(event.notification);
1981                });
1982
1983                // Handle cold-start opens (notification tapped before JS attached)
1984                try {
1985                    const lastOpen = await window.plugins.OneSignal.Notifications.getLastNotification();
1986                    if (lastOpen?.notification) {
1987                        console.log("Handling last opened notification on init");
1988                        handleOneSignalOpen(lastOpen.notification);
1989                    }
1990                } catch (e) {
1991                    console.warn("Unable to read last opened notification", e);
1992                }
1993
1994                // Listen for subscription changes
1995                window.plugins.OneSignal.User.pushSubscription.addEventListener("change", async (event) => {
1996                    if (event.current?.id) {
1997                        console.log("Subscription ID changed:", event.current.id);
1998                        await saveSubscriptionId(event.current.id);
1999                    }
2000                });
2001
2002                oneSignalReady = true;
2003                await syncOneSignalSubscription();
2004
2005            } catch (err) {
2006                console.error("OneSignal Error:", err);
2007                oneSignalInitStarted = false; // allow retry
2008            }
2009        }
2010
2011        async function syncOneSignalSubscription() {
2012            try {
2013                const subscriptionId = await window.plugins.OneSignal.User.pushSubscription.getIdAsync();
2014                if (subscriptionId) await saveSubscriptionId(subscriptionId);
2015            } catch (error) {
2016                console.error('Could not read OneSignal subscription:', error);
2017            }
2018        }
2019
2020        /**
2021         * Save subscription ID - Simple & Effective!
2022         */
2023        async function saveSubscriptionId(subscriptionId) {
2024            // Let the server check the current session; data-navigate-once scripts
2025            // must not freeze the authentication state of the initial page.
2026        try {
2027            const response = await fetch("/save/onesignal", {
2028                method: "POST",
2029                headers: {
2030                    "Content-Type": "application/json",
2031                    "Accept": "application/json"
2032                },
2033                body: JSON.stringify({
2034                    subscription_id: subscriptionId
2035                })
2036            });
2037
2038            if (response.status === 401) return;
2039
2040            if (!response.ok) {
2041                const text = await response.text();
2042                console.error("? Server error:", response.status, text.substring(0, 200));
2043                return;
2044            }
2045
2046            const result = await response.json();
2047            if (result.status === 'success') {
2048                console.log("? Subscription saved");
2049            } else {
2050                console.error("? Failed:", result.message);
2051            }
2052        } catch (error) {
2053            console.error("? Error:", error);
2054        }
2055        }
2056
2057        // Initialize OneSignal on page load
2058        document.addEventListener("DOMContentLoaded", initOneSignal);
2059        document.addEventListener('deviceready', initOneSignal);
2060        document.addEventListener('livewire:navigated', initOneSignal);
2061        document.body.addEventListener("htmx:load", initOneSignal);
2062    </script>
2062
2063
2064
2065
2066
2067    
2068    
2068<script data-navigate-once>
2069        let specialSchemeClickHandler = null;
2070
2071        function initSpecialSchemeHandlers() {
2072            if (!window.Capacitor) return;
2073
2074            // Remove existing handler first to prevent duplicates
2075            if (specialSchemeClickHandler) {
2076                document.removeEventListener("click", specialSchemeClickHandler);
2077            }
2078
2079            // Handle tel:, mailto:, sms: links
2080            specialSchemeClickHandler = async (e) => {
2081                const link = e.target.closest('a[href^="tel:"], a[href^="mailto:"], a[href^="sms:"]');
2082                if (!link) return;
2083
2084                e.preventDefault();
2085                const url = link.href;
2086
2087                try {
2088                    // Open in external system app (phone dialer, email client, sms app)
2089                    await Capacitor.Plugins.InAppBrowser.openInExternalBrowser({
2090                        url
2091                    });
2092                    console.log('? Opened special scheme URL:', url);
2093                } catch (err) {
2094                    console.error('? Error opening special scheme URL:', err);
2095                    // Fallback: try window.open
2096                    window.open(url, '_system');
2097                }
2098            };
2099            document.addEventListener("click", specialSchemeClickHandler);
2100            console.log('? Special scheme handlers initialized (tel:, mailto:, sms:)');
2101        }
2102
2103        document.addEventListener("DOMContentLoaded", initSpecialSchemeHandlers);
2104        document.addEventListener('livewire:navigated', initSpecialSchemeHandlers);
2105        document.body.addEventListener("htmx:afterSwap", initSpecialSchemeHandlers);
2106    </script>
2106
2107
2108    
2109    
2109<script data-navigate-once>
2110        (function initDeepLinks() {
2111            if (!window.Capacitor?.Plugins?.App) return;
2112
2113            const {
2114                App
2115            } = window.Capacitor.Plugins;
2116
2117            function isAllowedHost(hostname) {
2118                return hostname === 'lave.live' || hostname.endsWith('.lave.live');
2119            }
2120
2121            function toWebUrl(incomingUrl) {
2122                if (!incomingUrl) return null;
2123
2124                // Handle lave://https://lave.live/... format (full URL embedded after scheme)
2125                if (incomingUrl.startsWith('lave://https://') || incomingUrl.startsWith('lave://http://')) {
2126                    const realUrl = incomingUrl.replace(/^lave:\/\//, '');
2127                    try {
2128                        const parsed = new URL(realUrl);
2129                        if (isAllowedHost(parsed.hostname)) return parsed.toString();
2130                    } catch (_) {
2131                        // fall through
2132                    }
2133                }
2134
2135                let u;
2136                try {
2137                    u = new URL(incomingUrl);
2138                } catch (_) {
2139                    return null;
2140                }
2141
2142                // Direct https links (if someone passes full URL)
2143                if ((u.protocol === 'https:' || u.protocol === 'http:') && isAllowedHost(u.hostname)) {
2144                    return u.toString();
2145                }
2146
2147                // Custom scheme: lave://home/app OR lave://open?url=https%3A%2F%2Flave.live%2F...
2148                if (u.protocol === 'lave:') {
2149                    // Check for embedded URL param first
2150                    const embedded = u.searchParams.get('url');
2151                    if (embedded) {
2152                        try {
2153                            const embeddedUrl = new URL(embedded);
2154                            if (isAllowedHost(embeddedUrl.hostname)) return embeddedUrl.toString();
2155                        } catch (_) {
2156                            // ignore invalid embedded URL
2157                        }
2158                    }
2159
2160                    // Convert lave://some/path?x=1#hash => https://lave.live/some/path?x=1#hash
2161                    // u.host contains the first path segment (e.g. "home" from lave://home/app)
2162                    const path = `/${u.host}${u.pathname}`.replace(/\/{2,}/g, '/');
2163                    return `https://lave.live${path}${u.search}${u.hash}`;
2164                }
2165
2166                return null;
2167            }
2168
2169            function navigateFromDeepLink(url) {
2170                const target = toWebUrl(url);
2171                if (!target) return;
2172                console.log('🔗 Deep link received, navigating to:', target);
2173                window.location.href = target;
2174            }
2175
2176            // Cold start - app was closed, opened via deep link
2177            App.getLaunchUrl()
2178                .then(result => {
2179                    if (result?.url) navigateFromDeepLink(result.url);
2180                })
2181                .catch(() => {});
2182
2183            // Warm start - app was in background, opened via deep link
2184            App.addListener('appUrlOpen', (event) => {
2185                if (event?.url) navigateFromDeepLink(event.url);
2186            });
2187
2188            console.log('? Deep link handler initialized');
2189        })();
2190    </script>
2190
2191
2192    
2193    
2193<script data-navigate-once>
2194        let targetBlankClickHandler = null;
2195        let targetBlankSubmitHandler = null;
2196
2197        function initTargetBlankHandlers() {
2198            if (!window.Capacitor) return;
2199
2200            // Remove existing handlers first to prevent duplicates
2201            if (targetBlankClickHandler) {
2202                document.removeEventListener("click", targetBlankClickHandler, true);
2203            }
2204            if (targetBlankSubmitHandler) {
2205                document.removeEventListener("submit", targetBlankSubmitHandler, true);
2206            }
2207
2208            // Handle _blank target for <a> elements
2209            // Use capture phase to catch events early and prevent default browser behavior
2210            targetBlankClickHandler = async (e) => {
2211                const link = e.target.closest('a[target="_blank"]');
2212                if (!link) return;
2213
2214                // Skip if it's a special scheme (handled by specialSchemeClickHandler)
2215                if (link.href && (link.href.startsWith('tel:') || link.href.startsWith('mailto:') || link.href
2216                        .startsWith('sms:'))) {
2217                    return;
2218                }
2219
2220                // Prevent default immediately to stop browser from opening
2221                e.preventDefault();
2222                e.stopPropagation();
2223
2224                const url = link.href || link.getAttribute('href');
2225                if (!url) return;
2226
2227                try {
2228                    // Check if native="external_browser" attribute is set to use external browser
2229                    if (link.getAttribute("native") === "external_browser") {
2230                        await Capacitor.Plugins.InAppBrowser.openInExternalBrowser({
2231                            url: url
2232                        });
2233                        console.log('✅ Opened in external browser:', url);
2234                    } else {
2235                        // Default: Use InAppBrowser System Browser (Custom Tabs for Android, SFSafariViewController for iOS)
2236                        // This opens in-app but uses system browser components, not external browser
2237                        await Capacitor.Plugins.Browser.open({
2238                            url,
2239                            options: {
2240                                showToolbar: true,
2241                                toolbarPosition: "TOP"
2242                            }
2243                        });
2244                    }
2245                } catch (err) {
2246                    alert("Browser error:", err);
2247                }
2248            };
2249            // Use capture phase (true) to catch events before they bubble
2250            document.addEventListener("click", targetBlankClickHandler, true);
2251
2252        }
2253
2254        document.addEventListener("DOMContentLoaded", initTargetBlankHandlers);
2255        document.addEventListener('livewire:navigated', initTargetBlankHandlers);
2256        document.body.addEventListener("htmx:afterSwap", initTargetBlankHandlers);
2257    </script>
2257
2258
2259    
2260    
2260<script data-navigate-once>
2261        let capacitorFileClickHandler = null;
2262
2263        function initFileInput() {
2264            if (!window.Capacitor) return;
2265
2266            const fileModal = document.getElementById("fileModal");
2267            let currentInput = null;
2268            let tempInputs = []; // Keep references to prevent garbage collection
2269
2270            // Create modal if it doesn't exist
2271            if (!fileModal) {
2272                const modal = document.createElement("div");
2273                modal.id = "fileModal";
2274                modal.className = "fixed inset-0 hidden items-center justify-center bg-black bg-opacity-60 px-4";
2275                modal.style.zIndex = "2147483647";
2276                modal.innerHTML = `
2277                    <div class="relative top-40 mx-auto w-full max-w-md overflow-hidden rounded-2xl bg-white shadow-xl">
2278                        <div class="flex items-center justify-between border-b border-gray-200 px-5 py-3">
2279                            <div class="text-base font-semibold text-gray-800">Select file</div>
2280                            <button onclick="closeModal('fileModal')" class="text-2xl font-light text-gray-400 hover:text-gray-600" type="button">×</button>
2281                        </div>
2282                        <div class="divide-y divide-gray-100">
2283                            <button onclick="choosePhoto()" class="flex w-full items-center gap-3 px-5 py-3 text-left text-gray-700 hover:bg-gray-50 active:bg-gray-100">
2284                                <span class="text-xl">📸</span>
2285                                <span class="text-sm">Take photo with camera</span>
2286                            </button>
2287                            <button onclick="chooseFromLibrary()" class="flex w-full items-center gap-3 px-5 py-3 text-left text-gray-700 hover:bg-gray-50 active:bg-gray-100">
2288                                <span class="text-xl">🖼️</span>
2289                                <span class="text-sm">Select photo from gallery</span>
2290                            </button>
2291                            <button onclick="recordVideo()" class="flex w-full items-center gap-3 px-5 py-3 text-left text-gray-700 hover:bg-gray-50 active:bg-gray-100">
2292                                <span class="text-xl">📹</span>
2293                                <span class="text-sm">Record video</span>
2294                            </button>
2295                            <button onclick="chooseFile()" class="flex w-full items-center gap-3 px-5 py-3 text-left text-gray-700 hover:bg-gray-50 active:bg-gray-100">
2296                                <span class="text-xl">📁</span>
2297                                <span class="text-sm">Choose file</span>
2298                            </button>
2299                        </div>
2300                    </div>
2301                `;
2302                document.body.appendChild(modal);
2303            }
2304
2305            const modal = document.getElementById("fileModal");
2306
2307            // Delegation also covers inputs inserted by Livewire modal renders.
2308            if (capacitorFileClickHandler) {
2309                document.removeEventListener('click', capacitorFileClickHandler);
2310            }
2311            capacitorFileClickHandler = (e) => {
2312                const inp = e.target.closest('input[type="file"]');
2313                if (!inp || inp._capacitorNativePicker) return;
2314                e.preventDefault();
2315                currentInput = inp;
2316                if (modal) modal.classList.remove('hidden');
2317            };
2318            document.addEventListener('click', capacitorFileClickHandler);
2319
2320            // Click outside modal to close
2321            if (modal) {
2322                if (modal._modalClickHandler) {
2323                    modal.removeEventListener("click", modal._modalClickHandler);
2324                }
2325
2326                modal._modalClickHandler = (e) => {
2327                    if (e.target === modal) modal.classList.add("hidden");
2328                };
2329                modal.addEventListener("click", modal._modalClickHandler);
2330            }
2331
2332            // Helper function to inject file into input
2333            function injectFile(file, targetInput) {
2334                if (!targetInput?.isConnected) {
2335                    console.error('No file input available to inject file');
2336                    return;
2337                }
2338                try {
2339                    const dt = new DataTransfer();
2340                    dt.items.add(file);
2341                    targetInput.files = dt.files;
2342                    targetInput.dispatchEvent(new Event("change", {
2343                        bubbles: true
2344                    }));
2345                    console.log(`Selected: ${file.name}`);
2346                } catch (error) {
2347                    console.error('Error injecting file:', error);
2348                }
2349            }
2350
2351            // Helper to create temp input and inject file
2352            function createAndInjectFile(inputEl) {
2353                const targetInput = currentInput;
2354                inputEl._capacitorNativePicker = true;
2355                // Add input to DOM (hidden) to prevent garbage collection
2356                inputEl.style.position = 'absolute';
2357                inputEl.style.left = '-9999px';
2358                inputEl.style.opacity = '0';
2359                inputEl.style.pointerEvents = 'none';
2360                document.body.appendChild(inputEl);
2361
2362                // Keep reference to prevent garbage collection
2363                tempInputs.push(inputEl);
2364
2365                // Use addEventListener for better reliability
2366                const changeHandler = (e) => {
2367                    if (inputEl.files && inputEl.files.length > 0) {
2368                        injectFile(inputEl.files[0], targetInput);
2369                    }
2370                    // Clean up after use
2371                    inputEl.removeEventListener('change', changeHandler);
2372                    setTimeout(() => {
2373                        if (inputEl.parentNode) {
2374                            inputEl.parentNode.removeChild(inputEl);
2375                        }
2376                        const index = tempInputs.indexOf(inputEl);
2377                        if (index > -1) {
2378                            tempInputs.splice(index, 1);
2379                        }
2380                    }, 100);
2381                };
2382
2383                inputEl.addEventListener('change', changeHandler);
2384
2385                // Small delay to ensure modal is closed and file picker can open
2386                setTimeout(() => {
2387                    inputEl.click();
2388                }, 100);
2389            }
2390
2391            // Global functions for modal buttons
2392            window.choosePhoto = function() {
2393                if (!currentInput) {
2394                    console.error('No file input selected');
2395                    return;
2396                }
2397                if (modal) modal.classList.add("hidden");
2398                // Use native file input - doesn't interfere with status bar
2399                const input = document.createElement("input");
2400                input.type = "file";
2401                input.accept = "image/*";
2402                input.capture = "environment"; // Opens camera directly
2403                createAndInjectFile(input);
2404            };
2405
2406            window.chooseFromLibrary = function() {
2407                if (!currentInput) {
2408                    console.error('No file input selected');
2409                    return;
2410                }
2411                if (modal) modal.classList.add("hidden");
2412                // Use native file input - doesn't interfere with status bar
2413                const input = document.createElement("input");
2414                input.type = "file";
2415                input.accept = "image/*";
2416                createAndInjectFile(input);
2417            };
2418
2419            window.recordVideo = function() {
2420                if (!currentInput) {
2421                    console.error('No file input selected');
2422                    return;
2423                }
2424                if (modal) modal.classList.add("hidden");
2425                // Use native file input - doesn't interfere with status bar
2426                const input = document.createElement("input");
2427                input.type = "file";
2428                input.accept = "video/*";
2429                input.capture = "environment"; // Opens camera for video
2430                createAndInjectFile(input);
2431            };
2432
2433            window.chooseFile = function() {
2434                if (!currentInput) {
2435                    console.error('No file input selected');
2436                    return;
2437                }
2438                if (modal) modal.classList.add("hidden");
2439                // Use native file input - simple and reliable
2440                const input = document.createElement("input");
2441                input.type = "file";
2442                createAndInjectFile(input);
2443            };
2444
2445            window.closeModal = function(modalId) {
2446                const m = document.getElementById(modalId);
2447                if (m) m.classList.add("hidden");
2448            };
2449        }
2450
2451        document.addEventListener("DOMContentLoaded", initFileInput);
2452        document.addEventListener('livewire:navigated', initFileInput);
2453        document.body.addEventListener("htmx:afterSwap", initFileInput);
2454    </script>
2454
2455
2456    
2457    
2457<script data-navigate-once>
2458        function updateCapacitorCountryCode(select) {
2459            const form = select.closest('form') || select.parentElement;
2460            const hiddenField = form?.querySelector('input[name="tel_country_code"]');
2461            const selectedOption = select.options[select.selectedIndex];
2462            if (!hiddenField || !selectedOption) return;
2463
2464            hiddenField.value = selectedOption.getAttribute('data-phonecode') || '';
2465            hiddenField.dispatchEvent(new Event('input', { bubbles: true }));
2466        }
2467
2468        // The select may be rendered after page initialization by Livewire.
2469        document.addEventListener('change', (event) => {
2470            const select = event.target.closest('select[name="country"]');
2471            if (select) updateCapacitorCountryCode(select);
2472        });
2473
2474        function initCountrySelect() {
2475            document.querySelectorAll('select[name="country"]').forEach(updateCapacitorCountryCode);
2476        }
2477
2478        // Initialize on page load and after HTMX swaps
2479        document.addEventListener('DOMContentLoaded', initCountrySelect);
2480        document.addEventListener('livewire:navigated', initCountrySelect);
2481        document.body.addEventListener('htmx:afterSwap', initCountrySelect);
2482    </script>
2482
2483
2484    
2485    
2485<script data-navigate-once>
2486        let contactPickerClickHandler = null;
2487
2488        function initContactPicker() {
2489            if (!window.Capacitor) return;
2490
2491            const contactPickerModal = document.getElementById('contactPickerModal');
2492            const contactList = document.getElementById('contactList');
2493            const contactNameHeader = document.getElementById('contactNameHeader');
2494            const closeModalPhoneBtn = document.getElementById('closeModalPhone');
2495
2496            function opencontactPickerModal(contactName, phones, onSelect) {
2497                if (!contactList) return;
2498                contactList.innerHTML = '';
2499                if (contactNameHeader) {
2500                    contactNameHeader.textContent = contactName || 'Select Phone Number';
2501                    contactNameHeader.title = contactName;
2502                }
2503
2504                phones.forEach((number, index) => {
2505                    const btn = document.createElement('button');
2506                    btn.className =
2507                        "w-full text-left px-5 py-4 text-base text-gray-800 hover:bg-blue-50 active:bg-blue-100 transition-colors flex items-center gap-3";
2508                    btn.type = "button";
2509
2510                    // Add phone icon
2511                    const icon = document.createElement('span');
2512                    icon.innerHTML = '📱';
2513                    icon.className = "text-xl";
2514
2515                    const text = document.createElement('span');
2516                    text.textContent = number;
2517                    text.className = "flex-1";
2518
2519                    btn.appendChild(icon);
2520                    btn.appendChild(text);
2521
2522                    btn.addEventListener('click', () => {
2523                        closecontactPickerModal();
2524                        onSelect(number);
2525                    });
2526                    contactList.appendChild(btn);
2527                });
2528
2529                if (contactPickerModal) {
2530                    contactPickerModal.classList.remove('hidden');
2531                    contactPickerModal.classList.add('flex');
2532                }
2533            }
2534
2535            function closecontactPickerModal() {
2536                if (contactPickerModal) {
2537                    contactPickerModal.classList.add('hidden');
2538                    contactPickerModal.classList.remove('flex');
2539                }
2540            }
2541
2542            // Close button handler
2543            if (closeModalPhoneBtn) {
2544                // Remove existing listener first
2545                if (closeModalPhoneBtn._clickHandler) {
2546                    closeModalPhoneBtn.removeEventListener('click', closeModalPhoneBtn._clickHandler);
2547                }
2548                closeModalPhoneBtn._clickHandler = closecontactPickerModal;
2549                closeModalPhoneBtn.addEventListener('click', closeModalPhoneBtn._clickHandler);
2550            }
2551
2552            // Click outside to close
2553            if (contactPickerModal) {
2554                if (contactPickerModal._outsideClickHandler) {
2555                    contactPickerModal.removeEventListener('click', contactPickerModal._outsideClickHandler);
2556                }
2557                contactPickerModal._outsideClickHandler = (e) => {
2558                    if (e.target === contactPickerModal) {
2559                        closecontactPickerModal();
2560                    }
2561                };
2562                contactPickerModal.addEventListener('click', contactPickerModal._outsideClickHandler);
2563            }
2564
2565            // Remove existing contact picker handler
2566            if (contactPickerClickHandler) {
2567                document.removeEventListener('click', contactPickerClickHandler);
2568            }
2569
2570            contactPickerClickHandler = async (e) => {
2571                const el = e.target.closest('[native="contact"]');
2572                if (!el) return;
2573
2574                e.preventDefault();
2575
2576                const contactForm = el.closest('form');
2577                if (!contactForm) return;
2578
2579                const {
2580                    Contacts
2581                } = window.Capacitor.Plugins || {};
2582                if (!Contacts) {
2583                    console.log('Contacts plugin not available');
2584                    return;
2585                }
2586
2587                // Function to pick contact
2588                async function tryPickContact() {
2589                    return await Contacts.pickContact({
2590                        projection: {
2591                            name: true,
2592                            phones: true,
2593                            emails: true
2594                        }
2595                    });
2596                }
2597
2598                try {
2599                    // ✅ Try pickContact() first - it should handle permissions automatically
2600                    const result = await tryPickContact();
2601                    await processContactResult(result);
2602                } catch (err) {
2603                    console.error("Contact picker error:", err);
2604
2605                    // 🔄 Fallback: If permission error, request permission and retry
2606                    if (err.message && err.message.toLowerCase().includes('permission')) {
2607                        console.log('Permission error detected, requesting permissions...');
2608
2609                        try {
2610                            const permission = await Contacts.checkPermissions();
2611                            console.log('Current permission:', permission);
2612
2613                            if (permission.contacts !== 'granted') {
2614                                const requested = await Contacts.requestPermissions();
2615                                console.log('Permission requested:', requested);
2616
2617                                if (requested.contacts !== 'granted') {
2618                                    alert(
2619                                        '📱 To select contacts, please allow access in your device settings.\n\nSettings > Apps > Lave > Permissions > Contacts > Full Access'
2620                                    );
2621                                    return;
2622                                }
2623                            }
2624
2625                            // Retry with permission granted
2626                            const result = await tryPickContact();
2627                            await processContactResult(result);
2628
2629                        } catch (permErr) {
2630                            console.error("Permission request failed:", permErr);
2631                            alert(
2632                                '📱 To select contacts, please allow access in your device settings.\n\nSettings > Apps > Lave > Permissions > Contacts > Full Access'
2633                            );
2634                        }
2635                    } else {
2636                        // Different error, show it
2637                        alert('❌ ERROR picking contact:\n\n' + err.message);
2638                    }
2639                }
2640
2641                // Process contact result
2642                async function processContactResult(result) {
2643
2644                    if (result?.contact) {
2645
2646                        const rawName = result.contact.name?.display || 'No name';
2647                        const phones = result.contact.phones?.map(p => p.number) || [];
2648                        const emails = result.contact.emails?.map(e => e.address) || [];
2649
2650
2651                        // Function to keep only alphanumeric characters and spaces (removes emojis & special chars)
2652                        function stripEmojis(text) {
2653                            return text
2654                                .replace(/[^a-zA-Z0-9\s.,'-]/g,
2655                                    '') // Keep only letters, numbers, space
2655s, and basic punctuation
2656                                .trim()
2657                                .replace(/\s+/g, ' '); // Clean up extra spaces
2658                        }
2659
2660                        const name = stripEmojis(rawName);
2661
2662                        // Function to extract country code by comparing with select options
2663                        function extractCountryCodeFromSelect(phoneNumber, countrySelect) {
2664                            // Remove all spaces, dashes, parentheses, and + sign
2665                            let cleaned = phoneNumber.replace(/[\s\-\(\)\+]/g, '');
2666
2667                            // If no digits, return empty
2668                            if (!cleaned || !/^\d+$/.test(cleaned)) {
2669                                return {
2670                                    countryCode: null,
2671                                    phone: cleaned,
2672                                    matchFound: false
2673                                };
2674                            }
2675
2676                            // Get all country codes from the select options
2677                            const options = countrySelect ? countrySelect.options : [];
2678                            const countryCodes = [];
2679
2680                            // Preferred countries for shared codes (main countries over territories)
2681                            const preferredCountries = [
2682                                'United Kingdom', 'United States', 'Canada', 'Australia',
2683                                'France', 'Denmark', 'Netherlands', 'New Zealand'
2684                            ];
2685
2686                            for (let i = 0; i < options.length; i++) {
2687                                const phonecode = options[i].getAttribute('data-phonecode');
2688                                const countryName = options[i].value || options[i].textContent.trim();
2689                                if (phonecode) {
2690                                    const isPreferred = preferredCountries.some(pref =>
2691                                        countryName.toLowerCase().includes(pref.toLowerCase())
2692                                    );
2693                                    countryCodes.push({
2694                                        code: phonecode,
2695                                        index: i,
2696                                        name: countryName,
2697                                        isPreferred: isPreferred
2698                                    });
2699                                }
2700                            }
2701
2702                            // Sort by:
2703                            // 1. Code length (longest first) - e.g., 1684 before 1
2704                            // 2. Preferred countries first (for same length codes)
2705                            // 3. Index (original order)
2706                            countryCodes.sort((a, b) => {
2707                                if (b.code.length !== a.code.length) {
2708                                    return b.code.length - a.code.length;
2709                                }
2710                                if (a.isPreferred !== b.isPreferred) {
2711                                    return b.isPreferred ? 1 : -1;
2712                                }
2713                                return a.index - b.index;
2714                            });
2715
2716                            // Try to find a matching country code
2717                            for (const country of countryCodes) {
2718                                if (cleaned.startsWith(country.code)) {
2719                                    // Found a match!
2720                                    const phone = cleaned.substring(country.code.length);
2721                                    return {
2722                                        countryCode: country.code,
2723                                        phone: phone,
2724                                        matchFound: true,
2725                                        countryIndex: country.index,
2726                                        countryName: country.name
2727                                    };
2728                                }
2729                            }
2730
2731                            // No match found, return the whole number as-is
2732                            return {
2733                                countryCode: null,
2734                                phone: cleaned,
2735                                matchFound: false
2736                            };
2737                        }
2738
2739                        // Function to populate form fields
2740                        function populateContactForm(selectedPhone) {
2741                            // The picker may return after navigation or after its form has closed.
2742                            if (!contactForm.isConnected) return;
2743
2744                            const nameInput = contactForm.querySelector('input[name="name"]');
2745                            const emailInput = contactForm.querySelector('input[name="email"]');
2746                            const telInput = contactForm.querySelector('input[name="tel"]');
2747                            const countrySelect = contactForm.querySelector('select[name="country"]');
2748                            const countryCodeInput = contactForm.querySelector('input[name="tel_country_code"]');
2749
2750                            function updateInput(input, value) {
2751                                if (!input) return;
2752                                input.value = value;
2753                                input.dispatchEvent(new Event('input', { bubbles: true }));
2754                            }
2755
2756                            // Populate name
2757                            updateInput(nameInput, name);
2758
2759                            // Populate email
2760                            if (emails.length > 0) updateInput(emailInput, emails[0]);
2761
2762                            // Process phone number and country code
2763                            if (selectedPhone) {
2764                                const result = extractCountryCodeFromSelect(selectedPhone, countrySelect);
2765
2766                                // Populate phone number (without country code)
2767                                updateInput(telInput, result.phone);
2768
2769                                // Set country based on matched country code
2770                                if (result.matchFound && countrySelect) {
2771                                    // Set the dropdown
2772                                    countrySelect.selectedIndex = result.countryIndex;
2773
2774                                    // Directly update the hidden field with the country code
2775                                    if (countryCodeInput && result.countryCode) {
2776                                        updateInput(countryCodeInput, result.countryCode);
2777                                    }
2778
2779                                    // Also trigger the change event as backup
2780                                    countrySelect.dispatchEvent(new Event('change', { bubbles: true }));
2781
2782                                    console.log('✅ Country set to:', result.countryName, '(+' + result
2783                                        .countryCode + ')');
2784                                }
2785                            }
2786                        }
2787
2788                        if (phones.length === 0) {
2789                            // No phone number, just populate name and email
2790                            populateContactForm('');
2791                        } else if (phones.length === 1) {
2792                            // Single phone number, populate immediately
2793                            populateContactForm(phones[0]);
2794                        } else {
2795                            // Multiple phone numbers, show modal
2796                            opencontactPickerModal(name, phones, selected => {
2797                                populateContactForm(selected);
2798                            });
2799                        }
2800                    } else {
2801                        // No contact in result
2802                        alert('⚠️ No contact found');
2803                    }
2804                }
2805            };
2806            document.addEventListener('click', contactPickerClickHandler);
2807        }
2808
2809        document.addEventListener("DOMContentLoaded", initContactPicker);
2810        document.addEventListener('livewire:navigated', initContactPicker);
2811        document.body.addEventListener("htmx:afterSwap", initContactPicker);
2812    </script>
2812
2813
2814
2815    
2816    
2816<script data-navigate-once>
2817        function initCapacitorButtons() {
2818            if (!window.Capacitor) return;
2819
2820            const btnDevice = document.getElementById('btnDevice');
2821            if (btnDevice && !btnDevice._capacitorHandlerAttached) {
2822                btnDevice._capacitorHandlerAttached = true;
2823                btnDevice.addEventListener('click', async () => {
2824                    try {
2825                        if (window.Capacitor.Plugins.Device) {
2826                            const info = await window.Capacitor.Plugins.Device.getInfo();
2827                            alert(JSON.stringify(info, null, 2));
2828                        } else alert(JSON.stringify({
2829                            platform: 'web',
2830                            userAgent: navigator.userAgent
2831                        }, null, 2));
2832                    } catch (err) {
2833                        alert("Error: " + err);
2834                    }
2835                });
2836            }
2837
2838        }
2839
2840        document.addEventListener("DOMContentLoaded", initCapacitorButtons);
2841        document.addEventListener('livewire:navigated', initCapacitorButtons);
2842        document.body.addEventListener("htmx:afterSwap", initCapacitorButtons);
2843    </script>
2843
2844
2845    
2846    
2846<script data-navigate-once>
2847        /**
2848         * Reveal the parsed page after a paint opportunity. Images and other
2849         * non-critical resources can finish loading with the page visible.
2850         */
2851        let splashScreenHidden = false;
2852        let splashScreenHiding = false;
2853
2854        async function hideSplashScreen() {
2855            // Prevent multiple calls
2856            if (splashScreenHidden || splashScreenHiding) return;
2857
2858            // Wait for Capacitor to be available
2859            if (!window.Capacitor) {
2860                setTimeout(hideSplashScreen, 100);
2861                return;
2862            }
2863
2864            // Wait for SplashScreen plugin to be available
2865            if (!window.Capacitor.Plugins || !window.Capacitor.Plugins.SplashScreen) {
2866                setTimeout(hideSplashScreen, 100);
2867                return;
2868            }
2869
2870            splashScreenHiding = true;
2871            try {
2872                // Wait for DOM to be ready
2873                if (document.readyState === 'loading') {
2874                    await new Promise(resolve => {
2875                        if (document.readyState === 'loading') {
2876                            document.addEventListener('DOMContentLoaded', resolve);
2877                        } else {
2878                            resolve();
2879                        }
2880                    });
2881                }
2882
2883                // Give the browser a chance to paint before revealing the page.
2884                await new Promise(resolve => requestAnimationFrame(resolve));
2885                await new Promise(resolve => requestAnimationFrame(resolve));
2886
2887                // Hide the splash screen
2888                await window.Capacitor.Plugins.SplashScreen.hide();
2889                splashScreenHidden = true;
2890                console.log('✅ Splash screen hidden - initial page ready');
2891            } catch (error) {
2892                console.warn('Could not hide splash screen:', error);
2893                // Try one more time after a delay
2894                setTimeout(async () => {
2895                    try {
2896                        if (!splashScreenHidden && window.Capacitor?.Plugins?.SplashScreen) {
2897                            await window.Capacitor.Plugins.SplashScreen.hide();
2898                            splashScreenHidden = true;
2899                            console.log('✅ Splash screen hidden (retry)');
2900                        }
2901                    } catch (retryError) {
2902                        console.error('Failed to hide splash screen after retry:', retryError);
2903                    }
2904                }, 1000);
2905            } finally {
2906                splashScreenHiding = false;
2907            }
2908        }
2909
2910        // Start checking when script loads
2911        if (document.readyState === 'loading') {
2912            document.addEventListener('DOMContentLoaded', hideSplashScreen, { once: true });
2913        } else {
2914            // DOM is already ready, start the hide process
2915            hideSplashScreen();
2916        }
2917
2918        // Fallback: ensure splash screen is hidden after window fully loads
2919        window.addEventListener('load', hideSplashScreen, { once: true });
2920
2921        // Retry initialization if an earlier readiness hook was missed.
2922        setTimeout(() => {
2923            if (splashScreenHidden) return;
2924            console.warn('Fallback splash hide after timeout');
2925            hideSplashScreen();
2926        }, 4000);
2927    </script>
2927
2928
2929    
2930    
2930<script data-navigate-once>
2931        // Apply any pending deep link without waiting for HTMX
2932        (function applyPendingDeepLink() {
2933            const DEEPLINK_KEY = 'pending_deeplink';
2934            let deepLinkApplied = false;
2935
2936            async function apply(delayMs = 0) {
2937                if (deepLinkApplied) return;
2938
2939                // Small optional delay to let bootstrapping settle
2940                if (delayMs > 0) {
2941                    await new Promise(resolve => setTimeout(resolve, delayMs));
2942                }
2943
2944                const target = localStorage.getItem(DEEPLINK_KEY);
2945                if (!target) return;
2946
2947                try {
2948                    localStorage.removeItem(DEEPLINK_KEY);
2949                } catch (_) {}
2950
2951                deepLinkApplied = true;
2952                console.log('Applying pending deep link', target);
2953                window.location.href = target;
2954            }
2955
2956            const applySoon = () => apply(50);
2957            const applyNow = () => apply(0);
2958
2959            if (document.readyState === 'loading') {
2960                document.addEventListener('DOMContentLoaded', applySoon);
2961            } else {
2962                applySoon();
2963            }
2964
2965            window.addEventListener('load', applyNow);
2966            window.addEventListener('focus', applyNow);
2967            document.addEventListener('visibilitychange', applyNow);
2968
2969            // When app returns to foreground (Capacitor)
2970            if (window.Capacitor?.Plugins?.App) {
2971                try {
2972                    window.Capacitor.Plugins.App.addListener('appStateChange', ({
2973                        isActive
2974                    }) => {
2975                        if (isActive) applyNow();
2976                    });
2977                } catch (e) {
2978                    console.warn('App state listener failed', e);
2979                }
2980            }
2981
2982            // Periodic safety check in case other hooks miss
2983            setTimeout(applyNow, 500);
2984            setTimeout(applyNow, 1500);
2985        })();
2986    </script>
2986
2987
2988</div>
2989
2990    
2990<script>
2991        (function() {
2992            const isPWA =
2993                window.matchMedia('(display-mode: standalone)').matches ||
2994                window.navigator.standalone === true;
2995
2996            if (isPWA) {
2997                document.cookie = "app_mode=pwa; path=/; max-age=31536000";
2998            }
2999        })();
3000    </script>
3000
3001
3002    
3003
3004
3005
3006    <!-- Search settings modal container -->
3007    
3007<script>
3008        /**
3009         * Global Shadow DOM Content Renderer
3010         * Processes elements with .shadow-content and .raw-content
3011         */
3012        function renderShadowContent(container = document) {
3013            // Ensure we have a valid element to search in
3014            const searchRoot = (container && container.querySelectorAll) ? container : document;
3015
3016            const targets = (searchRoot.classList && searchRoot.classList.contains('shadow-content')) ? [searchRoot] :
3017                searchRoot.querySelectorAll('.shadow-content');
3018
3019            // If we're processing a specific container and find nothing, fallback once to the whole document.
3020            if (targets.length === 0 && searchRoot !== document) {
3021                renderShadowContent(document);
3022                return;
3023            }
3024
3025            targets.forEach(host => {
3026                if (host.shadowRoot) return; // Skip if already processed
3027
3028                const dataElement = host.parentElement.querySelector('.raw-content');
3029                if (!dataElement) return;
3030
3031                try {
3032                    const shadow = host.attachShadow({
3033                        mode: 'open'
3034                    });
3035                    const content = JSON.parse(dataElement.textContent);
3036
3037                    const style = document.createElement('style');
3038                    style.textContent = `
3039
3040                     img {
3041                                    max-width: 100% !important;
3042                                    height: auto !important;
3043                                    display: block !important;
3044                                    object-fit: contain !important;
3045                                    margin: auto !important;
3046                                }
3047                                iframe, video {
3048                                    display: table !important;
3049                                    margin-left: auto !important;
3050                                    margin-right: auto !important;
3051                                    max-width: 100% !important;
3052                                    object-fit: contain !important;
3053                                }
3054                   `;
3055
3056                    const wrapper = document.createElement('div');
3057                    wrapper.innerHTML = content;
3058
3059                    shadow.appendChild(style);
3060                    shadow.appendChild(wrapper);
3061                } catch (e) {
3062                    console.error('Shadow DOM Error:', e);
3063                }
3064            });
3065        }
3066
3067        document.addEventListener('livewire:init', () => {
3068            renderShadowContent();
3069            Livewire.hook('morphed', ({
3070                el
3071            }) => renderShadowContent(el));
3072        });
3073
3074        document.addEventListener('livewire:navigated', () => renderShadowContent());
3075    </script>
3075
3076
3077
3078    
3078<script>
3079        document.addEventListener('livewire:init', () => {
3080            const refreshUIkit = (root) => {
3081                window.requestAnimationFrame(() => {
3082                    if (!window.UIkit || !root?.isConnected) {
3083                        return;
3084                    }
3085
3086                    const targets = [];
3087
3088                    if (isUIkitComponent(root)) {
3089                        targets.push(root);
3090                    }
3091
3092                    root.querySelectorAll('*').forEach((element) => {
3093                        if (isUIkitComponent(element)) {
3094                            targets.push(element);
3095                        }
3096                    });
3097
3098                    targets.forEach((element) => window.UIkit.update(element));
3099                });
3100            };
3101
3102            const isUIkitManagedGraphic = (element) => {
3103                if (element.matches(
3104                        '[uk-img], [data-uk-img], [uk-svg], [data-uk-svg], [uk-spinner], [data-uk-spinner]'
3105                    )) {
3106                    return true;
3107                }
3108
3109                return Array.from(element.attributes).some(({
3110                    name
3111                }) => {
3112                    return /^(data-)?uk-(.+-)?icon$/.test(name);
3113                });
3114            };
3115
3116            const isUIkitComponent = (element) => {
3117                return Array.from(element.attributes).some(({
3118                    name
3119                }) => {
3120                    return /^(data-)?uk-/.test(name);
3121                });
3122            };
3123
3124            Livewire.hook('morph.updating', ({
3125                el,
3126                skip,
3127                childrenOnly
3128            }) => {
3129                if (isUIkitManagedGraphic(el)) {
3130                    skip();
3131
3132                    return;
3133                }
3134
3135                if (isUIkitComponent(el)) {
3136                    childrenOnly();
3137                }
3138            });
3139
3140            Livewire.hook('morphed', ({
3141                el
3142            }) => {
3143                refreshUIkit(el);
3144            });
3145
3146            Livewire.hook('partial.morphed', ({
3147                startNode
3148            }) => {
3149                refreshUIkit(startNode.parentElement);
3150            });
3151
3152            Livewire.hook('morph.added', ({
3153                el
3154            }) => {
3155                refreshUIkit(el);
3156            });
3157        });
3158    </script>
3158
3159
3160    
3160<script>
3161        document.addEventListener('livewire:init', () => {
3162            const pendingSubmitComponents = new Set();
3163
3164            document.addEventListener('submit', (event) => {
3165                const form = event.target;
3166
3167                if (!(form instanceof HTMLFormElement)) {
3168                    return;
3169                }
3170
3171                const isLivewireForm = Array.from(form.attributes)
3172                    .some((attribute) => attribute.name === 'wire:submit' || attribute.name.startsWith(
3173                        'wire:submit.'));
3174
3175                if (!isLivewireForm) {
3176                    return;
3177                }
3178
3179                const component = form.closest('[wire\\:id]');
3180
3181                if (component) {
3182                    pendingSubmitComponents.add(component.getAttribute('wire:id'));
3183                }
3184            }, true);
3185
3186            Livewire.interceptMessage(({
3187                message,
3188                onSuccess
3189            }) => {
3190                const component = message.component;
3191
3192                if (!pendingSubmitComponents.delete(component.id)) {
3193                    return;
3194                }
3195
3196                onSuccess(({
3197                    onRender
3198                }) => {
3199                    onRender(() => {
3200                        const messages = component.$wire.$errors.all();
3201                        const firstError = Array.from(component.el.querySelectorAll(
3202                                'p, span, small, div, strong'))
3203                            .find((element) => messages.includes(element.textContent
3204                                .trim()));
3205
3206                        firstError?.scrollIntoView({
3207                            behavior: 'smooth',
3208                            block: 'center',
3209                        });
3210                    });
3211                });
3212            });
3213        });
3214    </script>
3214
3215
3216    
3216<script src="https://lave.live/vendor/livewire/livewire.min.js?id=0ae8dee3"   data-csrf="AjMCTFOftFon9p8sIzSEEUc4qAhUkKFeGD31X2bH" data-module-url="https://lave.live/livewire-af6929b1" data-update-uri="https://lave.live/livewire-af6929b1/update" data-navigate-once="true"></script>
3216
3217
3218    
3218<script>
3219    window.$zoho = window.$zoho || {};
3220    window.__zohoChatPending = false;
3221    window.__zohoChatSyncTimer = null;
3222
3223    window.__showZohoChat = function() {
3224        if (window.$zoho?.salesiq?.floatwindow?.visible) {
3225            window.$zoho.salesiq.floatwindow.visible('show');
3226            return true;
3227        }
3228
3229        return false;
3230    };
3231
3232    window.openZohoChat = function(event) {
3233        if (event) {
3234            event.preventDefault();
3235        }
3236
3237        if (window.__showZohoChat()) {
3238            return false;
3239        }
3240
3241        window.__zohoChatPending = true;
3242        return false;
3243    };
3244
3245    $zoho.salesiq = $zoho.salesiq || {};
3246    $zoho.salesiq.ready = function() {
3247        if (window.$zoho?.salesiq?.floatbutton?.visible) {
3248            window.$zoho.salesiq.floatbutton.visible('hide');
3249        }
3250
3251        if (window.__zohoChatPending) {
3252            window.__zohoChatPending = false;
3253            window.__showZohoChat();
3254        }
3255    };
3256</script>
vendor: 1 bytes, line 3256
3256
3257<script id="zsiqscript" src="https://salesiq.zohopublic.com/widget?wc=siq1cec881323d29b6db65f0661720c7d07" defer>
3258</script>
3258
3259
3260    
3260<script>
3261if (!window.laveUploadSizeGuardInstalled) {
3262    window.laveUploadSizeGuardInstalled = true;
3263    document.addEventListener('change', function (event) {
3264        const input = event.target;
3265        if (!(input instanceof HTMLInputElement) || input.type !== 'file' || !input.dataset.uploadMaxKb) return;
3266        const limit = Number(input.dataset.uploadMaxKb);
3267        if (Array.from(input.files || []).some(file => file.size > limit * 1024)) {
3268            event.preventDefault();
3269            event.stopImmediatePropagation();
3270            input.value = '';
3271            alert('This file is too large. Maximum file size is :size MB.'.replace(':size', String(Math.round(limit / 1024 * 100) / 100)));
3272        }
3273    }, true);
3274}
3275</script>
vendor: 1 bytes, line 3275
3275
3276<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a4401bc2cad2f32d',t:'MTc5MDkwNjM3Mw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
3276</body>
3277
3278</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.