PageSourceSearch

https://lave.live/online-store

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