PageSourceSearch

https://loklokpro.com/

html loklokpro.com collected 2026-09-29 13:34:07 UTC 832,296 bytes, 24,630 lines download raw bytes

1<!DOCTYPE html>
2<html lang="id">
3<head>
4  <meta charset="UTF-8">
5  <title>LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL </title>
6  <meta name="description" content="IWANTOGEL Merupakan Tempat Kumpulnya Vidio Game Online Terlaris di seluruh Dunia Dengan Updatenya permainan Terbaru dan Vidio live Score Setiap harinya Serta Dukungan Akses Stabil Dan navigasi Sederhana Login Dan nikmati Bersama IWANTOGEL Sekarang juga!!.">
7  <meta name="robots" content="index,follow">
8  <link rel="canonical" href="https://loklokpro.com/">
9  <meta name="google-site-verification" content="dS8NukqmhZE0Xlt2hv7aaHu5Z0skABDq-RAMgf49UEE" />
10  <meta name="google-site-verification" content="PffTTNBgs-gxR6cDaxhyzXsSVv307G8bSFLMuSWIpp0" />
11  <meta name="google-site-verification" content="v6fbSXU8gHq89C6OKR-4e8x6MtPIjhh35GErg9hvevM" />
12  <meta name="google-site-verification" content="lIQOZHaCPZr0Idg1pXY9Wx-CCh6BAfCa1zBpHMZ9GOc" />
13  <meta name="keywords" content="iwantogel, dracin terbaru,donghua terbaru" />
14  <link rel="amphtml" href="https://slot-gacor.loklokpro.com/">
15  <link rel="alternate" href="https://slot-gacor.loklokpro.com/" hreflang="id">
16  <link rel="alternate" href="https://slot-gacor.loklokpro.com/" hreflang="en">
17  <link rel="alternate" href="https://slot-gacor.loklokpro.com/" hreflang="x-default">
18  <link rel="alternate" hreflang="id-ID" href="https://slot-gacor.loklokpro.com/" />
19  <link rel="alternate" hreflang="en-US" href="https://slot-gacor.loklokpro.com/" />
20  <link rel="alternate" hreflang="en-SG" href="https://slot-gacor.loklokpro.com/" />
21    <link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
vendor: 66 bytes, lines 21-22
21
22  <script async src="https://www.googletagmanager.com/gtag/js?id=
22G-0LSM1YJSPZ
vendor: 47 bytes, lines 22-23
22" nonce="se3iyzEHMc9FQEB4wSyd0w=="></script>
23  
23<script nonce="se3iyzEHMc9FQEB4wSyd0w==">window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("consent","default",{ad_storage:"denied",ad_user_data:"denied",ad_personalization:"denied",analytics_storage:"granted",wait_for_update:500});gtag("js",new Date);gtag("config","G-0LSM1YJSPZ",{send_page_view:!0,linker:{domains:["plazait.co.id","www.plazait.co.id"]}});window.ga4Ecomm={viewItem:function(e){if(!window.gtag)return;try{gtag("event","view_item",{currency:e.currency||"IDR",value:e.value||0,items:[{item_id:e.item_id||"",item_name:e.item_name||"",item_brand:e.item_brand||"",item_category:e.item_category||"",price:e.price||0,quantity:1}]})}catch(t){}},addToCart:function(e){if(!window.gtag)return;try{gtag("event","add_to_cart",{currency:e.currency||"IDR",value:(e.price||0)*(e.quantity||1),items:[{item_id:e.item_id||"",item_name:e.item_name||"",item_brand:e.item_brand||"",item_category:e.item_category||"",price:e.price||0,quantity:e.quantity||1}]})}catch(t){}}};</script>
23
24  <meta property="og:locale" content="id_ID">
25  <meta property="og:site_name" content="TOTO TOGEL">
26  <meta property="og:type" content="product">
27  <meta property="og:title" content="LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL ">
28  <meta property="og:description" content="IWANTOGEL Merupakan Tempat Kumpulnya Vidio Game Online Terlaris di seluruh Dunia Dengan Updatenya permainan Terbaru dan Vidio live Score Setiap harinya Serta Dukungan Akses Stabil Dan navigasi Sederhana Login Dan nikmati Bersama IWANTOGEL Sekarang juga!!.">
29  <meta property="og:url" content="https://loklokpro.com/">
30  <meta property="og:image" content="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png">
31  <meta property="og:image:alt" content="LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL ">
32<meta property="product:price:amount" content="5000">
33<meta property="product:price:currency" content="IDR">
34<meta property="product:availability" content="out of stock">
35<meta name="google-site-verification" content="xZhbOCnLFUisoQt6G1ewazv5WqmwQ-bdG6l_-u84nqw" />
36<meta name="twitter:card" content="summary_large_image">
37<meta name="twitter:title" content="LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL ">
38<meta name="twitter:description" content="IWANTOGEL Merupakan Tempat Kumpulnya Vidio Game Online Terlaris di seluruh Dunia Dengan Updatenya permainan Terbaru dan Vidio live Score Setiap harinya Serta Dukungan Akses Stabil Dan navigasi Sederhana Login Dan nikmati Bersama IWANTOGEL Sekarang juga!!.">
39<meta name="twitter:image" content="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png">
40
41<script type="application/ld+json">
42      {
43        "@context": "http:\/\/schema.org",
44        "@type": "BreadcrumbList",
45        "itemListElement": [
46          {
47            "@type": "ListItem",
48            "item": {
49              "@id": "https://loklokpro.com/",
50              "name": "LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL "
51            },
52            "position": 1
53          }
54        ]
55      }
56    </script>
56
57    
57<script type="application/ld+json">
58      {
59        "@context": "http:\/\/schema.org",
60        "@type": "Product",
61        "name": "LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL ",
62        "description": "IWANTOGEL Merupakan Tempat Kumpulnya Vidio Game Online Terlaris di seluruh Dunia Dengan Updatenya permainan Terbaru dan Vidio live Score Setiap harinya Serta Dukungan Akses Stabil Dan navigasi Sederhana Login Dan nikmati Bersama IWANTOGEL Sekarang juga!!.",
63        "image": "https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png",
64        "offers": {
65          "@type": "http:\/\/schema.org\/Offer",
66          "price": 9999,
67          "url": "https://loklokpro.com/",
68          "priceCurrency": "IDR",
69          "availability": "http:\/\/schema.org\/InStock"
70        }
71      }
72    </script>
72
73    
73<script type="application/ld+json">
74      {
75        "@context": "http:\/\/schema.org\/",
76        "@type": "WebPage",
77        "speakable": {
78          "@type": "SpeakableSpecification",
79          "cssSelector": [".description"],
80          "xpath": ["\/html\/head\/title"]
81        }
82      }
83    </script>
83
84    <!-- ZS -->
85    <!-- Schema JSON untuk SBO -->
86<script type="application/ld+json">
87{
88"@context": "https://schema.org/",
89"@type": "Product",
90"name": "LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL ",
91"url": "https://loklokpro.com/",
92"sku": "IWANTOGEL",
93"image": [
94  "https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png"
95],
96"description": "IWANTOGEL Merupakan Tempat Kumpulnya Vidio Game Online Terlaris di seluruh Dunia Dengan Updatenya permainan Terbaru dan Vidio live Score Setiap harinya Serta Dukungan Akses Stabil Dan navigasi Sederhana Login Dan nikmati Bersama IWANTOGEL Sekarang juga!!.",
97"brand": {
98  "@type": "Organization",
99  "name": "IWAN TOGEL"
100},
101"offers": {
102  "@type": "Offer",
103  "name": "LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL ",
104  "sku": "LOGIN IWANTOGEL",
105  "price": "9999",
106  "priceCurrency": "IDR",
107  "availability": "https://schema.org/InStock"
108}
109}
110</script>
110
111
112<script type="application/ld+json">
113{
114"@context": "https://schema.org/",
115"@type": "Product",
116"name": "LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL ",
117"image": "https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png",
118"description": "IWANTOGEL Merupakan Tempat Kumpulnya Vidio Game Online Terlaris di seluruh Dunia Dengan Updatenya permainan Terbaru dan Vidio live Score Setiap harinya Serta Dukungan Akses Stabil Dan navigasi Sederhana Login Dan nikmati Bersama IWANTOGEL Sekarang juga!!.",
119"sku": "TOGEL ONLINE",
120"offers": {
121  "@type": "Offer",
122  "priceCurrency": "IDR",
123  "url": "https://loklokpro.com/",
124  "availability": "https://schema.org/InStock",
125  "price": "9999.00"
126}
127}
128</script>
128
129
130<script type="application/ld+json" id="bv-jsonld-bvloader-summary">
131{
132"@context": "https://schema.org/",
133"@id": "https://loklokpro.com/",
134"aggregateRating": {
135  "@type": "AggregateRating",
136  "itemReviewed": {
137    "@type": "IndividualProduct",
138    "name": "IWANTOGEL"
139  },
140  "ratingValue": "5.0",
141  "reviewCount": 954638,
142  "bestRating": 5
143}
144}
145</script>
145
146
147<script type="application/ld+json">
148{
149"@context": "https://schema.org",
150"@type": "BreadcrumbList",
151"itemListElement": [
152  {
153    "@type": "ListItem",
154    "position": 1,
155    "item": {
156      "@id": "https://loklokpro.com/",
157      "name": "IWANTOGEL"
158    }
159  },
160  {
161    "@type": "ListItem",
162    "position": 2,
163    "item": {
164      "@id": "https://loklokpro.com/",
165      "name": "LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL "
166    }
167  },
168  {
169    "@type": "ListItem",
170    "position": 3,
171    "item": {
172      "@id": "https://loklokpro.com/",
173      "name": "IWAN TOGEL"
174    }
175  },
176  {
177    "@type": "ListItem",
178    "position": 4,
179    "item": {
180      "@id": "https://loklokpro.com/",
181      "name": "LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL "
182    }
183  }
184]
185}
186</script>
186
187
188<script type="application/ld+json" id="bv-jsonld-reviews-data">
189{
190  "@context": "https://schema.org",
191  "@type": "Organization",
192  "@id": "https://loklokpro.com/",
193  "name": "LOGIN IWANTOGEL",
194  "alternateName": "BANDAR TOGEL ONLINE",
195  "url": "https://loklokpro.com/",
196  "review": [
197    {
198      "@type": "Review",
199      "dateCreated": "2026-03-31T12:28:35+00:00",
200      "datePublished": "2026-03-31T12:28:35+00:00",
201      "headline": "DAFTAR IWANTOGEL",
202      "reviewBody": "IWANTOGEL benar-benar memudahkan akses login bandar togel online terpercaya 24 jam nonstop antarmuka yang elegan dan proses masuk yang cepat membuat saya bisa bertaruh kapan saja tanpa gangguan teknis sehingga pengalaman bermain terasa lebih mewah dan efisien.",
203      "reviewRating": {
204        "@type": "Rating",
205        "ratingValue": 5,
206        "bestRating": 5,
207        "worstRating": 1
208      },
209      "author": {
210        "@type": "Person",
211        "name": "Taufan – Surabaya"
212      }
213    },
214    {
215      "@type": "Review",
216      "dateCreated": "2026-04--02T18:28:21+00:00",
217      "datePublished": "2026-04-03T18:28:21+00:00",
218      "headline": "LINK IWANTOGEL",
219      "reviewBody": "Saya mengapresiasi bagaimana IWANTOGEL menyediakan proses deposit cepat yang jelas dan transparan; konfirmasi transaksi berjalan instan sehingga waktu menunggu berkurang dan pemain bisa langsung fokus pada strategi permainan.",
220      "reviewRating": {
221        "@type": "Rating",
222        "ratingValue": 5,
223        "bestRating": 5,
224        "worstRating": 1
225      },
226      "author": {
227        "@type": "Person",
228        "name": "Thomas Tampi – Semarang"
229      }
230    },
231    {
232      "@type": "Review",
233      "dateCreated": "2026-04-05T19:28:18+00:00",
234      "datePublished": "2026-04-05T19:28:18+00:00",
235      "headline": "SLOT GACOR",
236      "reviewBody": "Keamanan data di IWANTOGEL memberi rasa tenang yang luar biasa; fitur enkripsi dan proteksi akun bekerja baik sehingga informasi pribadi dan riwayat transaksi tetap aman, membuat platform ini tampak sangat profesional dan dapat dipercaya.",
237      "reviewRating": {
238        "@type": "Rating",
239        "ratingValue": 5,
240        "bestRating": 5,
241        "worstRating": 1
242      },
243      "author": {
244        "@type": "Person",
245        "name": "Soesilowati – Makassar"
246      }
247    }
248  ]
249}
250</script>
vendor: 1 bytes, line 250
250
251<script type="application/ld+json">
252{
253  "@context": "https://schema.org",
254  "@type": "FAQPage",
255  "mainEntity": [
256    {
257  "@type": "Question",
258  "name": "Apa itu IWANTOGEL dan mengapa disebut bandar togel online terpercaya 24 jam nonstop?",
259  "acceptedAnswer": {
260    "@type": "Answer",
261    "text": "IWANTOGEL adalah platform bandar togel online yang menyediakan akses login 24 jam nonstop, didesain untuk kemudahan pengguna dengan antarmuka elegan dan sistem yang handal. Disebut terpercaya karena menerapkan protokol keamanan, verifikasi transaksi, dan layanan pelanggan penuh waktu yang memastikan pengalaman taruhan yang aman, konsisten, dan minim gangguan."
262  }
263},
264{
265  "@type": "Question",
266  "name": "Bagaimana proses deposit cepat bekerja di IWANTOGEL dan apa manfaatnya bagi pemain?",
267  "acceptedAnswer": {
268    "@type": "Answer",
269    "text": "Proses deposit cepat di IWANTOGEL mengandalkan integrasi dengan metode pembayaran modern dan sistem konfirmasi otomatis sehingga dana tercatat hampir seketika. Manfaatnya termasuk pengurangan waktu tunggu sebelum taruhan bisa ditempatkan, minimnya risiko kehilangan peluang taruhan, dan alur bermain yang lebih efisien serta menyenangkan."
270  }
271},
272{
273  "@type": "Question",
274  "name": "Seberapa kuat perlindungan keamanan data di IWANTOGEL dan fitur apa yang digunakan?",
275  "acceptedAnswer": {
276    "@type": "Answer",
277    "text": "IWANTOGEL menerapkan enkripsi tingkat tinggi, proteksi berlapis untuk autentikasi pengguna, dan kebijakan privasi ketat yang melindungi informasi pribadi serta transaksi. Fitur seperti enkripsi SSL/TLS, pemantauan aktivitas mencurigakan, dan kontrol akses membantu meminimalkan risiko kebocoran data dan penipuan, sehingga pemain dapat bermain dengan tenang."
278  }
279},
280{
281  "@type": "Question",
282  "name": "Apakah layanan pelanggan IWANTOGEL benar-benar tersedia 24 jam nonstop dan saluran apa saja yang bisa digunakan?",
283  "acceptedAnswer": {
284    "@type": "Answer",
285    "text": "Ya, IWANTOGEL menawarkan layanan pelanggan 24 jam nonstop melalui beberapa saluran seperti live chat, email, dan pusat bantuan di situs. Tim dukungan dilatih untuk menangani pertanyaan teknis, panduan deposit, dan masalah akun secara responsif sehingga pemain mendapatkan bantuan cepat untuk menjaga kelancaran taruhan."
286  }
287},
288{
289  "@type": "Question",
290  "name": "Bagaimana IWANTOGEL memastikan pengalaman login yang mudah namun tetap aman?",
291  "acceptedAnswer": {
292    "@type": "Answer",
293    "text": "IWANTOGEL menggabungkan desain antarmuka yang intuitif dengan langkah keamanan seperti verifikasi dua faktor bila diperlukan, enkripsi data, dan pemeriksaan aktivitas tidak biasa. Pendekatan ini memastikan pengguna dapat masuk dengan cepat tanpa mengorbankan integritas akun, sehingga akses login tetap nyaman dan terlindungi."
294  }
295}
296  ]
297}
298</script>
298
299  <!-- User Session Data -->
300    <meta name="csrf-token" content="9ce6e4a17f7cc189893df9e47789b13e98829450d516a0cae5ca7b5f43e5dadc">
301  <meta name="user-logged-in" content="false">
302  <meta name="user-id" content="0">
303  
304  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
305
306  <link rel="shortcut icon" href="https://area-terpercaya.b-cdn.net/newicon-iwantogel.png" type="image/x-icon">
307  <style data-inlined="style.css">
308@font-face {
309    font-family: 'Segoe UI';
310    src: url('/assets/fonts/segoeui.woff2') format('woff2'),
311         url('/assets/fonts/segoeui.woff') format('woff');
312    font-weight: 400;
313    font-style: normal;
314}
315
316:root {
317  --brand-50:#FFF5E6;
318  --brand-100:#FFE7CC;
319  --brand-500:#F28C1A;
320  --brand-600:#D97809;
321  --gray-900:#1F1F23;
322  --gray-700:#3D3F45;
323  --gray-500:#6F737B;
324  --gray-300:#C5C9CF;
325  --gray-100:#F3F5F7;
326  --border:#E4E7EB;
327  --radius:8px;
328  --transition: .18s ease;
329  /* font-family: "Inter", system-ui, sans-serif; */
330  font-family: 'Segoe UI', sans-serif;
331}
332
333* { box-sizing: border-box; }
334body { 
335  margin:0; 
336  padding-bottom: 0; /* Reset for desktop */
337  color:var(--gray-900); 
338  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%); 
339}
340img { max-width:100%; display:block; }
341
342.container {
343  width:100%;
344  max-width:1280px;
345  margin:0 auto;
346  padding:10px 0;
347}
348
349.topbar {
350  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
351  color:#fff;
352  font-size:13px;
353}
354.topbar-inner {
355  display:flex;
356  justify-content:space-between;
357  align-items:center;
358  min-height:36px;
359}
360.topbar a { color:#fff; text-decoration:none; margin-left:12px; }
361
362/* ========== Desktop base (Glassmorphism Modern with Warm Gradient) ========== */
363.navbar {
364  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
365  backdrop-filter:blur(12px);
366  -webkit-backdrop-filter:blur(12px);
367  border-bottom:1px solid rgba(228,231,235,0.6);
368  box-shadow:0 2px 16px -4px rgba(0,0,0,0.08);
369  position:sticky;
370  top:0;
371  z-index:500;
372  transition:all .3s ease;
373}
374
375/* Accent line gradient orange */
376.navbar::after {
377  content:'';
378  position:absolute;
379  bottom:0;
380  left:0;
381  right:0;
382  height:2px;
383  background:linear-gradient(90deg,transparent 0%, var(--brand-500) 20%, var(--brand-600) 50%, var(--brand-500) 80%, transparent 100%);
384  opacity:0.5;
385}
386
387.nav-inner {
388  display:flex;
389  align-items:center;
390  gap:24px;
391  min-height:70px;
392}
393.nav-inner .search .m-search-ico { display: none; }
394
395/* Secara default (desktop) ikon login disembunyikan; teks muncul */
396.login-btn .login-ico { display:none; }
397.navbar-logo {
398  display: none;
399  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
400  backdrop-filter:blur(12px);
401  -webkit-backdrop-filter:blur(12px);
402  border-bottom:1px solid rgba(228,231,235,0.6);
403  box-shadow:0 2px 16px -4px rgba(0,0,0,0.08);
404  position:sticky;
405  top:0;
406  z-index:500;
407}
408
409/* Hide bottom navigation on desktop - only show on mobile */
410.bottom-nav {
411  display: none;
412}
413
414/* ==============================================================
415   MOBILE (<=640px)
416   ============================================================== */
417@media (max-width:640px){
418  .container { padding:10px 16px; }
419  
420  .topbar { display:none !important; }
421
422  /* Add padding bottom for sticky bottom nav */
423  body {
424    padding-bottom: 68px; /* Height of bottom nav + safe area */
425  }
426
427  /* Mobile Top Navbar - Clean & Minimal */
428  .navbar {
429    background:linear-gradient(135deg, rgba(0,0,0,0.97) 0%, rgba(130, 22, 3,0.9) 100%);
430    backdrop-filter:blur(16px);
431    -webkit-backdrop-filter:blur(16px);
432    box-shadow:0 2px 12px -3px rgba(0,0,0,0.08);
433    border: 0;
434  }
435
436  /* Simple grid layout - Logo, Search, Category */
437  .nav-inner {
438    display:grid;
439    grid-template-columns:1fr;
440    gap:10px;
441    padding:12px 16px 0 12px;
442    min-height:auto;
443  }
444
445  /* Logo centered at top */
446  .nav-inner .logo {
447    justify-self:center;
448    margin:0;
449    display:flex;
450  }
451  .nav-inner .logo img { height:36px; }
452
453  /* Search bar - full width */
454  .nav-inner .search {
455    position:relative;
456    display:flex;
457    align-items:center;
458    gap:0;
459    width:100%;
460    height:44px;
461    background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
462    border:1.5px solid #e5e7eb;
463    border-radius:12px;
464    padding:0 12px;
465    box-shadow:0 1px 3px rgba(0,0,0,0.04);
466    transition:all .25s;
467  }
468  .nav-inner .search:focus-within {
469    border-color:#fb923c;
470    box-shadow:0 0 0 3px rgba(251, 146, 60, 0.1);
471  }
472
473  .nav-inner .search-icon-btn {
474    color:#9ca3af;
475    padding:4px;
476    margin-right:4px;
477  }
478  
479  .nav-inner .search-icon-btn svg {
480    width:18px;
481    height:18px;
482  }
483
484  .nav-inner .search input {
485    flex:1;
486    min-width:0;
487    border:none;
488    outline:none;
489    background:transparent;
490    font-size:14px;
491    padding:0 8px;
492    height:100%;
493  }
494  
495  .nav-inner .ai-bot-toggle {
496    padding:6px;
497    margin-left:0;
498  }
499  
500  .nav-inner .ai-bot-toggle .bot-icon {
501    width:20px;
502    height:20px;
503  }
504  
505  .nav-inner .search-suggest {
506    left:0;
507    right:0;
508    width:calc(100vw - 32px);
509    margin-top:6px;
510    border-radius:12px;
511    max-height:70vh;
512  }
513
514  /* Hide desktop nav actions but keep structure for userMenu */
515  .nav-inner .nav-actions {
516    display:flex !important; /* Keep flex to allow userMenu positioning */
517    visibility:hidden; /* Hide visually but keep in DOM */
518    pointer-events:none; /* Disable interactions with hidden elements */
519    position:absolute; /* Remove from flow */
520    right:0;
521    top:0;
522  }
523  
524  /* Re-enable userMenu for logged-in users */
525  .nav-inner .nav-actions #userMenu {
526    visibility:visible !important;
527    pointer-events:auto !important;
528  }
529  
530  .nav-inner .logo { display: none; }
531  .navbar-logo {
532    display: block;
533    /* background:linear-gradient(135deg, rgba(0,0,0,0.97) 0%, rgba(130, 22, 3,0.9) 100%); */
534    backdrop-filter:blur(16px);
535    -webkit-backdrop-filter:blur(16px);
536    position: static; /* Remove sticky on mobile */
537    box-shadow: none;
538    border-bottom: 0;
539    padding-top: .6rem;
540  }
541  .nav-inner .logo-mobile { display: block; }
542  
543  /* Category menu wrapper will handle visibility */
544
545  /* ==============================================================
546     BOTTOM NAVIGATION - Styles moved to bottom-nav.css
547     ============================================================== */
548  /* All bottom-nav styles now in /assets/css/bottom-nav.css */
549
550  /* Hide desktop elements that now moved to bottom nav */
551  #wishlistBtn,
552  #notifBtn,
553  #loginBtn { display:none !important; }
554  
555  /* Hide desktop user menu on mobile - use bottom sheet instead */
556  .navbar #userMenu { 
557    display:none !important;
558  }
559  
560  .nav-actions .cart-indicator,
561  .nav-actions .compare-indicator {
562    display:none !important;
563  }
564
565  /* Hide category button from bottom nav */
566  #bottomCategoryBtn {
567    display: none !important;
568  }
569  
570  /* User menu button styling for mobile - scoped to navbar only */
571  .navbar #userMenu .login-btn.logged-in {
572    width:40px;
573    height:40px;
574    padding:0;
575    border-radius:50%;
576    display:flex;
577    align-items:center;
578    justify-content:center;
579    background:rgba(0,0,0,0.95);
580    backdrop-filter:blur(12px);
581    -webkit-backdrop-filter:blur(12px);
582    border:1px solid rgba(228,231,235,0.6);
583    box-shadow:0 2px 12px -3px rgba(0,0,0,0.12);
584  }
585  
586  .navbar #userMenu .login-btn.logged-in .login-text { 
587    display:none; 
588  }
589  
590  .navbar #userMenu .login-btn.logged-in .login-ico {
591    display:inline;
592    font-size:20px;
593    line-height:1;
594  }
595  
596  /* User dropdown positioning for mobile */
597  .navbar #userMenu .user-dropdown {
598    position:fixed !important;
599    top:60px !important;
600    right:16px !important;
601    left:auto !important;
602    min-width:200px;
603    background:rgba(0,0,0,0.98);
604    backdrop-filter:blur(20px);
605    -webkit-backdrop-filter:blur(20px);
606    border:1px solid rgba(228,231,235,0.6);
607    box-shadow:0 8px 24px -8px rgba(0,0,0,0.2);
608    z-index:1002 !important;
609    border-radius:12px;
610    padding:8px 0;
611  }
612  
613  /* Force visibility when not hidden attribute */
614  .navbar #userMenu .user-dropdown:not([hidden]) {
615    display:block !important;
616    visibility:visible !important;
617    opacity:1 !important;
618  }
619
620  /* Container untuk kategori dan tombol kategori sejajar */
621  .navbar .container:last-child {
622    display:flex !important;
623    align-items:center !important;
624    padding:4px 16px 6px !important;
625    gap:5px;
626    overflow:visible;
627  }
628  
629  /* Wrapper untuk mega-cats dengan fade gradient (mobile only) */
630  .mega-cats-wrapper {
631    position:relative;
632    flex:1;
633    overflow:hidden;
634    min-height:34px; /* Ensure content is visible */
635    display:flex; /* Ensure wrapper is flex container */
636  }
637  
638  /* Fade gradient indicator di ujung kanan (mobile only) */
639  .mega-cats-fade {
640    position:absolute;
641    top:0;
642    right:0;
643    bottom:0;
644    width:80px; /* Increased from 60px for better visibility */
645    background:linear-gradient(to left, 
646      rgba(130, 22, 3,1) 0%, 
647      rgba(130, 22, 3,0.98) 15%,
648      rgba(130, 22, 3,0.85) 35%, 
649      transparent 100%);
650    pointer-events:none;
651    z-index:5; /* Increased z-index */
652    transition:opacity 0.3s ease;
653  }
654  
655  /* Kategori menu di mobile */
656  .mega-cats.container {
657    display:flex !important;
658    gap:12px;
659    overflow-x:auto;
660    overflow-y:visible;
661    -webkit-overflow-scrolling:touch;
662    scrollbar-width:none;
663    flex:1;
664    padding:0 !important;
665    margin:0 !important;
666    position:relative; /* For z-index stacking */
667    z-index:1; /* Below fade gradient */
668  }
669  .mega-cats.container::-webkit-scrollbar { display:none; }
670
671  /* Styling untuk menu items di mobile */
672  .mega-cats a {
673    font-size:13px;
674    padding:6px 10px;
675    white-space:nowrap;
676    flex-shrink:0;
677    position:relative; /* Ensure proper stacking */
678  }
679  .mega-cats a.active {
680    background:var(--brand-600);
681    color:#fff;
682    font-weight:600;
683    border-radius:6px;
684  }
685  
686  /* Tombol kategori di mobile */
687  .mm-wp {
688    flex-shrink:0;
689  }
690  .mm2-trigger {
691    display:flex;
692    align-items:center;
693    gap:6px;
694    padding:6px 12px;
695    background:var(--gray-100);
696    color:#FFFFFF;
697    text-decoration:none;
698    border-radius:6px;
699    font-size:13px;
700    white-space:nowrap;
701  }
702  
703  .mm2-trigger .label-cat {display: none; }
704  /* Tombol more kategori */
705  .mega-more-btn {
706    border:1px solid var(--border);
707    background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
708    border-radius:10px;
709    padding:6px 14px;
710    font-size:18px;
711    cursor:pointer;
712  }
713  .mega-cats-more.active > .mega-more-btn {
714    background:var(--brand-600);
715    color:#fff;
716    border-color:var(--brand-600);
717  }
718  .mega-more-dropdown {
719    top:calc(100% + 10px);
720    right:0;
721    min-width:85vw;
722    max-width:90vw;
723    border-radius:16px;
724  }
725  .mega-more-grid {
726    grid-template-columns:repeat(auto-fill,minmax(120px,1fr));
727  }
728  
729  /* ==============================================================
730     MOBILE USER BOTTOM SHEET
731     ============================================================== */
732  .mobile-user-sheet {
733    position:fixed;
734    top:0;
735    left:0;
736    right:0;
737    bottom:0;
738    z-index:10000;
739    pointer-events:none;
740  }
741  
742  .mobile-user-sheet.active {
743    pointer-events:auto;
744  }
745  
746  .mobile-user-sheet-backdrop {
747    position:absolute;
748    top:0;
749    left:0;
750    right:0;
751    bottom:0;
752    background:rgba(0,0,0,0.5);
753    opacity:0;
754    transition:opacity .3s ease;
755  }
756  
757  .mobile-user-sheet.active .mobile-user-sheet-backdrop {
758    opacity:1;
759  }
760  
761  .mobile-user-sheet-content {
762    position:absolute;
763    bottom:0;
764    left:0;
765    right:0;
766    background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
767    border-radius:20px 20px 0 0;
768    box-shadow:0 -4px 24px rgba(0,0,0,0.15);
769    transform:translateY(100%);
770    transition:transform .3s cubic-bezier(0.4, 0.0, 0.2, 1);
771    padding-bottom:calc(68px + env(safe-area-inset-bottom));
772  }
773  
774  .mobile-user-sheet.active .mobile-user-sheet-content {
775    transform:translateY(0);
776  }
777  
778  .mobile-user-sheet-header {
779    padding:16px 20px 12px;
780    border-bottom:1px solid #e5e7eb;
781  }
782  
783  .mobile-user-sheet-handle {
784    width:40px;
785    height:4px;
786    background:#d1d5db;
787    border-radius:2px;
788    margin:0 auto 16px;
789  }
790  
791  .mobile-user-info {
792    display:flex;
793    align-items:center;
794    gap:12px;
795  }
796  
797  .mobile-user-avatar {
798    width:48px;
799    height:48px;
800    border-radius:50%;
801    background:linear-gradient(135deg, #FFE7CC, #FFD9A8);
802    display:flex;
803    align-items:center;
804    justify-content:center;
805    font-size:24px;
806    flex-shrink:0;
807  }
808  
809  .mobile-user-details {
810    flex:1;
811    min-width:0;
812  }
813  
814  .mobile-user-name {
815    font-size:16px;
816    font-weight:600;
817    color:#111827;
818    margin-bottom:2px;
819    white-space:nowrap;
820    overflow:hidden;
821    text-overflow:ellipsis;
822  }
823  
824  .mobile-user-email {
825    font-size:13px;
826    color:#6b7280;
827    white-space:nowrap;
828    overflow:hidden;
829    text-overflow:ellipsis;
830  }
831  
832  .mobile-user-sheet-body {
833    padding:8px 0;
834  }
835  
836  .mobile-user-sheet-item {
837    display:flex;
838    align-items:center;
839    gap:16px;
840    padding:14px 20px;
841    text-decoration:none;
842    color:#374151;
843    font-size:15px;
844    transition:background .2s;
845    border:none;
846    background:none;
847    width:100%;
848    text-align:left;
849    cursor:pointer;
850  }
851  
852  .mobile-user-sheet-item:active {
853    background:#f3f4f6;
854  }
855  
856  .mobile-user-sheet-icon {
857    font-size:20px;
858    width:24px;
859    text-align:center;
860    flex-shrink:0;
861  }
862  
863  .mobile-user-sheet-label {
864    flex:1;
865    font-weight:500;
866  }
867  
868  .mobile-user-sheet-divider {
869    height:1px;
870    background:#e5e7eb;
871    margin:8px 0;
872  }
873}
874
875/* END NAVBAR */
876
877.logo img { display:block; }
878.search {
879  flex:1;
880  position:relative;
881  display:flex;
882  align-items:center;
883  gap:0;
884  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
885  border:1.5px solid #e5e7eb;
886  border-radius:12px;
887  padding:0 16px;
888  height:38px;
889  transition:all 0.3s ease;
890}
891.search:focus-within {
892  border-color:#fb923c;
893  box-shadow:0 0 0 3px rgba(251, 146, 60, 0.1);
894}
895.search input {
896  flex:1;
897  border:none;
898  outline:none;
899  background:transparent;
900  font-size:15px;
901  padding:0 12px;
902  height:100%;
903  color:#1f2937;
904}
905.search input::placeholder {
906  color:#9ca3af;
907}
908.search-icon-btn {
909  background:transparent;
910  border:none;
911  padding:0;
912  display:flex;
913  align-items:center;
914  justify-content:center;
915  cursor:pointer;
916  color:#6b7280;
917  transition:color 0.2s ease;
918  flex-shrink:0;
919}
920.search-icon-btn:hover {
921  color:#fb923c;
922}
923.search-suggest {
924  position:absolute;
925  left:0; right:0; top:100%;
926  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
927  border:1px solid var(--border);
928  border-radius:var(--radius);
929  padding:8px 0;
930  box-shadow:0 8px 20px rgba(0,0,0,.08);
931  margin-top:4px;
932  font-size:14px;
933  max-height:260px;
934  overflow:auto;
935}
936.search-suggest div {
937  padding:6px 12px;
938  cursor:pointer;
939}
940.search-suggest div:hover { background:var(--gray-100); }
941
942.nav-actions { display:flex; gap:10px; align-items:center; }
943.icon-btn, .login-btn {
944  background:linear-gradient(135deg, rgba(0,0,0,0.95), rgba(249,250,251,0.9));
945  border:1px solid rgba(228,231,235,0.6);
946  padding:8px 12px;
947  border-radius:var(--radius);
948  cursor:pointer;
949  font-size:14px;
950  transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
951  height: 38px;
952  position:relative;
953  box-shadow:
954    0 1px 3px rgba(0,0,0,0.06),
955    0 0 0 1px rgba(0,0,0,0.5) inset;
956  backdrop-filter:blur(8px);
957  -webkit-backdrop-filter:blur(8px);
958}
959.icon-btn:hover, .login-btn:hover { 
960  background:linear-gradient(135deg, rgba(249,250,251,1), rgba(243,244,246,0.95));
961  border-color:rgba(209,213,219,0.8);
962  box-shadow:
963    0 2px 8px rgba(0,0,0,0.08),
964    0 0 0 1px rgba(0,0,0,0.6) inset;
965  transform:translateY(-1px);
966}
967.cart-indicator, .compare-indicator { position:relative; }
968.badge {
969  background:linear-gradient(135deg, #EF4444, #DC2626);
970  color:#fff;
971  font-size:10.5px;
972  font-weight:600;
973  padding:2px 6px;
974  border-radius:999px;
975  position:absolute;
976  top:-6px; 
977  right:-6px;
978  min-width:18px;
979  height:18px;
980  display:flex;
981  align-items:center;
982  justify-content:center;
983  box-shadow:
984    0 2px 8px rgba(239,68,68,0.4),
985    0 0 0 2px #fff,
986    0 0 12px -2px rgba(239,68,68,0.6);
987  transition:all 0.3s ease;
988}
989.icon-btn:hover .badge {
990  transform:scale(1.08);
991  box-shadow:
992    0 4px 12px rgba(239,68,68,0.5),
993    0 0 0 2px #fff,
994    0 0 16px -2px rgba(239,68,68,0.7);
995}
996.badge.is-empty { display:none; }
997
998/* User Menu Dropdown */
999.user-menu {
1000  position:relative;
1001}
1002.user-dropdown {
1003  position:absolute;
1004  top:calc(100% + 8px);
1005  right:0;
1006  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
1007  border:1px solid var(--border);
1008  border-radius:12px;
1009  box-shadow:0 10px 40px rgba(0,0,0,0.12);
1010  min-width:200px;
1011  padding:8px 0;
1012  z-index:9999;
1013}
1014
1015/* Ensure dropdown is visible when not hidden */
1016.user-dropdown:not([hidden]) {
1017  display:block !important;
1018  visibility:visible !important;
1019  opacity:1 !important;
1020}
1021
1022/* Mobile: Force user dropdown to be visible above everything */
1023@media (max-width:640px) {
1024  .user-dropdown:not([hidden]) {
1025    position:fixed !important;
1026    z-index:9999 !important;
1027    top:60px !important;
1028    right:16px !important;
1029  }
1030}
1031
1032.dropdown-item {
1033  display:flex;
1034  align-items:center;
1035  gap:12px;
1036  padding:10px 16px;
1037  text-decoration:none;
1038  color:#FFFFFF;
1039  font-size:14px;
1040  transition:var(--transition);
1041  border:none;
1042  background:none;
1043  width:100%;
1044  text-align:left;
1045  cursor:pointer;
1046}
1047.dropdown-item:hover {
1048  background:var(--gray-100);
1049  color:var(--gray-900);
1050}
1051.dropdown-icon {
1052  font-size:16px;
1053  opacity:0.7;
1054}
1055.dropdown-separator {
1056  height:1px;
1057  background:var(--border);
1058  margin:8px 0;
1059}
1060.login-btn.logged-in {
1061  background:linear-gradient(135deg, rgba(130, 22, 3,0.8), rgba(255,231,204,0.6));
1062  border:1px solid rgba(242,140,26,0.2);
1063  color:var(--brand-700);
1064  max-width:200px; /* Prevent button from getting too wide */
1065  display:inline-flex;
1066  align-items:center;
1067  gap:10px;
1068  padding:4px 12px 4px 4px;
1069  transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1070  position:relative;
1071  overflow:hidden;
1072  box-shadow:
1073    0 1px 3px rgba(242,140,26,0.08),
1074    0 0 0 1px rgba(0,0,0,0.5) inset,
1075    0 0 12px -6px rgba(242,140,26,0.1);
1076  backdrop-filter:blur(8px);
1077  -webkit-backdrop-filter:blur(8px);
1078}
1079/* Subtle background glow effect */
1080.login-btn.logged-in::before {
1081  content:'';
1082  position:absolute;
1083  top:50%;
1084  left:0;
1085  width:32px;
1086  height:32px;
1087  background:radial-gradient(circle, rgba(242,140,26,0.15), transparent 70%);
1088  transform:translateY(-50%);
1089  opacity:0;
1090  transition:opacity 0.3s ease;
1091}
1092.login-btn.logged-in:hover::before {
1093  opacity:1;
1094}
1095.login-btn.logged-in .user-avatar {
1096  width:32px;
1097  height:32px;
1098  border-radius:50%;
1099  background:linear-gradient(135deg, #FFE7CC 0%, #FFD9A8 50%, #FFCC80 100%);
1100  color:#D97809;
1101  font-size:11.5px;
1102  font-weight:700;
1103  display:flex;
1104  align-items:center;
1105  justify-content:center;
1106  flex-shrink:0;
1107  letter-spacing:0.3px;
1108  position:relative;
1109  box-shadow:
1110    0 2px 8px rgba(242,140,26,0.25),
1111    0 1px 3px rgba(0,0,0,0.12),
1112    inset 0 1px 0 rgba(0,0,0,0.5);
1113  transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1114}
1115/* Ring gradient border */
1116.login-btn.logged-in .user-avatar::before {
1117  content:'';
1118  position:absolute;
1119  inset:-2px;
1120  border-radius:50%;
1121  padding:2px;
1122  background:linear-gradient(135deg, #F28C1A, #FFD9A8, #F28C1A);
1123  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
1124  -webkit-mask-composite:xor;
1125  mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
1126  mask-composite:exclude;
1127  opacity:0;
1128  transition:opacity 0.3s ease;
1129}
1130.login-btn.logged-in:hover .user-avatar::before {
1131  opacity:1;
1132}
1133.login-btn.logged-in:hover .user-avatar {
1134  transform:scale(1.08) rotate(-3deg);
1135  box-shadow:
1136    0 4px 12px rgba(242,140,26,0.35),
1137    0 2px 6px rgba(0,0,0,0.15),
1138    inset 0 1px 0 rgba(0,0,0,0.6);
1139}
1140.login-btn.logged-in .login-text {
1141  overflow:hidden;
1142  text-overflow:ellipsis;
1143  white-space:nowrap;
1144  flex:1;
1145  min-width:0;
1146  font-weight:500;
1147  font-size:14px;
1148  transition:color 0.2s ease;
1149}
1150.login-btn.logged-in:hover .login-text {
1151  color:var(--brand-600);
1152}
1153.login-btn.logged-in:hover {
1154  background:linear-gradient(135deg, rgba(130, 22, 3,0.95), rgba(255,231,204,0.85));
1155  border-color:rgba(242,140,26,0.3);
1156  box-shadow:
1157    0 4px 16px -4px rgba(242,140,26,0.25),
1158    0 0 0 1px rgba(242,140,26,0.1) inset,
1159    0 1px 2px rgba(0,0,0,0.05);
1160  transform:translateY(-1px);
1161}
1162
1163/* Desktop: hide wrapper elements, show menu normally */
1164@media (min-width: 761px) {
1165  .mega-cats-wrapper {
1166    display:contents; /* Remove wrapper from layout */
1167  }
1168  .mega-cats-fade {
1169    display:none !important; /* Hide fade on desktop */
1170  }
1171}
1172
1173.mega-cats {
1174  display:flex;
1175  gap:5px;
1176  /* padding:6px 16px 10px; */
1177  scrollbar-width:none;
1178  position: relative;
1179  z-index: 510;
1180  overflow-x:auto;
1181  -webkit-overflow-scrolling:touch;
1182  flex:1;
1183}
1184.mega-cats::-webkit-scrollbar { 
1185  display:none; 
1186}
1187.mega-cats a {
1188  text-decoration:none;
1189  font-size:14px;
1190  color:#FFFFFF;
1191  padding:6px 10px;
1192  border-radius:6px;
1193  white-space:nowrap;
1194  transition:var(--transition);
1195}
1196.mega-cats a:hover { background:var(--brand-100); color:var(--brand-600); }
1197/*.mega-cats::after {
1198  content:'';
1199  position:absolute;
1200  right:0;
1201  top:0;
1202  width:40px;
1203  height:100%;
1204  background:linear-gradient(to right,rgba(0,0,0,0),#fff);
1205  pointer-events:none;
1206}*/
1207
1208/* Tambahan untuk tombol More & dropdown.
1209   Style dasar .mega-cats dan .mega-cats a tetap pakai definisi Anda. */
1210
1211.mega-cats-more {
1212  position:relative;
1213  display:flex;
1214  align-items:center;
1215}
1216
1217.mega-cats-more.active > .mega-more-btn {
1218  background:var(--brand-600);
1219  color:#fff;
1220}
1221
1222.mega-more-btn {
1223  cursor:pointer;
1224  border:none;
1225  background:var(--gray-100,#f5f5f5);
1226  color:var(--gray-700,#333);
1227  padding:6px 12px;
1228  font-size:16px;
1229  line-height:1;
1230  border-radius:6px;
1231  display:flex;
1232  align-items:center;
1233  justify-content:center;
1234  transition:var(--transition);
1235}
1236.mega-more-btn:hover {
1237  background:var(--brand-100);
1238  color:var(--brand-600);
1239}
1240
1241.mega-more-dropdown {
1242  position:absolute;
1243  top:100%;
1244  right:0;
1245  margin-top:8px;
1246  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
1247  border:1px solid var(--gray-200,#e3e6e9);
1248  border-radius:10px;
1249  padding:12px 14px 14px;
1250  min-width:400px;
1251  max-width:520px;
1252  max-height:420px;
1253  box-shadow:0 12px 32px -8px rgba(25,40,65,.25);
1254  z-index: 3000; 
1255}
1256
1257.mega-more-grid {
1258  display:grid;
1259  grid-template-columns:repeat(auto-fill,minmax(120px,1fr));
1260  gap:8px 10px;
1261  overflow-x: hidden;
1262  max-height:380px;
1263  overscroll-behavior:contain;
1264  scrollbar-width:thin;
1265}
1266.mega-more-grid::-webkit-scrollbar {
1267  width:6px;
1268}
1269.mega-more-grid::-webkit-scrollbar-thumb {
1270  background:var(--gray-300,#c7ccd1);
1271  border-radius:3px;
1272}
1273
1274/* Link di dropdown mewarisi style .mega-cats a (warna, padding, radius).
1275   Kita tambahkan state active agar konsisten. */
1276.mega-more-grid a.active {
1277  background:var(--brand-600);
1278  color:#fff;
1279  font-weight:600;
1280}
1281.hero {
1282  background:linear-gradient(135deg,var(--brand-50), #fff);
1283}
1284.hero-inner {
1285  display:grid;
1286  grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
1287  gap:48px;
1288  align-items:center;
1289}
1290.hero-text h1 {
1291  font-size:48px;
1292  line-height:1.1;
1293  margin:0;
1294  font-weight:600;
1295  font-family:"Poppins",sans-serif;
1296}
1297.hero-text .text-brand { color:var(--brand-500); }
1298.lead { font-size:18px; color:#FFFFFF; margin-top:16px; }
1299.hero-cta { margin-top:28px; display:flex; gap:16px; }
1300.hero-bullets { margin-top:28px; display:flex; gap:28px; font-size:14px; color:var(--gray-600); }
1301.hero-box {
1302  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
1303  padding:10px;
1304  border-radius:16px;
1305  box-shadow:0 4px 18px rgba(0,0,0,.08);
1306  /* min-height:320px; */
1307  display:flex;
1308  align-items:center;
1309  justify-content:center;
1310}
1311.hero-box img {border-radius: 10px; }
1312
1313@media (max-width:680px) {
1314    .hero-inner { gap: 25px; }    
1315}
1316
1317.usp-section {
1318  position:relative;
1319  background:
1320    linear-gradient(180deg,#FFFDFB 0%, #FFFFFF 60%) ;
1321  border-top:1px solid var(--border);
1322  border-bottom:1px solid var(--border);
1323  padding:10px 0 10px;
1324}
1325
1326.usp-list {
1327  list-style:none;
1328  margin:0;
1329  padding:0;
1330  display:grid;
1331  gap:26px;
1332}
1333
1334.usp-list.variant-soft {
1335  grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
1336}
1337
1338.usp-item {
1339  display:flex;
1340  gap:16px;
1341  align-items:flex-start;
1342  background:#fff;
1343  padding:18px 18px 18px 16px;
1344  border:1px solid rgba(226,226,226,0.7);
1345  border-radius:18px;
1346  position:relative;
1347  overflow:hidden;
1348  isolation:isolate;
1349  box-shadow:0 2px 4px -1px rgba(0,0,0,.04), 0 6px 18px -6px rgba(0,0,0,.08);
1350  transition: .35s cubic-bezier(.16,.8,.32,1);
1351}
1352
1353.usp-item::before {
1354  content:"";
1355  position:absolute;
1356  inset:0;
1357  background:radial-gradient(circle at 18% 22%, rgba(242,140,26,.18), transparent 60%);
1358  opacity:.65;
1359  transition:.5s;
1360  z-index:-1;
1361}
1362
1363.usp-item:hover {
1364  transform:translateY(-6px);
1365  box-shadow:0 10px 26px -8px rgba(0,0,0,.15), 0 3px 8px -2px rgba(0,0,0,.08);
1366  border-color:rgba(242,140,26,.35);
1367}
1368
1369.usp-item:hover::before {
1370  background:radial-gradient(circle at 70% 40%, rgba(242,140,26,.22), transparent 65%);
1371}
1372
1373.usp-icon {
1374  flex:0 0 56px;
1375  width:56px;
1376  height:56px;
1377  border-radius:16px;
1378  display:flex;
1379  align-items:center;
1380  justify-content:center;
1381  background:
1382    linear-gradient(135deg,#FFE7CC,#FFD9A8);
1383  position:relative;
1384  box-shadow:inset 0 0 0 1px rgba(0,0,0,.6), 0 4px 10px -3px rgba(0,0,0,.15);
1385  transition:.45s;
1386}
1387
1388.usp-item:hover .usp-icon {
1389  transform:rotate(-4deg) scale(1.06);
1390  box-shadow:inset 0 0 0 1px rgba(0,0,0,.8), 0 6px 14px -4px rgba(0,0,0,.20);
1391}
1392
1393.usp-icon svg {
1394  width:28px;
1395  height:28px;
1396  display:block;
1397  filter:drop-shadow(0 2px 2px rgba(0,0,0,.18));
1398}
1399
1400.usp-text h3 {
1401  font-size:15px;
1402  font-weight:600;
1403  margin:2px 0 6px;
1404  letter-spacing:.2px;
1405}
1406
1407.usp-text p {
1408  font-size:13px;
1409  line-height:1.3;
1410  margin:0;
1411  color:var(--gray-600);
1412}
1413
1414@media (max-width:680px) {
1415  .usp-section { padding: 5px 0 5px; margin-bottom: 0.75rem;}
1416  .usp-item { padding:16px 14px; }
1417  .usp-icon { width:50px;height:50px;border-radius:14px; }
1418  .usp-text h3 { font-size:14px; margin-bottom:4px; }
1419  .usp-text p { font-size:12.5px; }
1420  .usp-list { gap: 10px; }
1421}
1422
1423.flash { padding:50px 0 30px; background:#fff; }
1424.flash-head {
1425  display:flex; align-items:center;
1426  justify-content:space-between;
1427  margin-bottom:20px;
1428}
1429.flash-head h2 { margin:0; font-size:24px; font-family:"Poppins"; }
1430.flash .countdown {
1431  font-family:monospace;
1432  background:#111;
1433  color:#fff;
1434  padding:8px 14px;
1435  border-radius:8px;
1436  font-size:16px;
1437  letter-spacing:1px;
1438}
1439.flash-scroll {
1440  display:flex;
1441  gap:16px;
1442  overflow-x:auto;
1443  padding-bottom:6px;
1444  scrollbar-width:none;
1445}
1446.flash-scroll::-webkit-scrollbar { display:none; }
1447
1448.products { padding:60px 0; background:#fff; }
1449.products-head {
1450  display:flex; justify-content:space-between; align-items:center;
1451  margin-bottom:24px;
1452  flex-wrap:wrap;
1453  gap:16px;
1454}
1455.products-head h2 { margin:0; font-size:24px; font-family:"Poppins"; }
1456.product-filters input {
1457  padding:8px 12px;
1458  border:1px solid var(--border);
1459  border-radius:8px;
1460  font-size:14px;
1461  width:220px;
1462}
1463
1464.product-grid {
1465  display:grid;
1466  gap:24px;
1467  grid-template-columns:repeat(auto-fill,minmax(180px,1fr));
1468}
1469
1470.prod-card {
1471  background:#fff;
1472  border:1px solid var(--border);
1473  padding:12px;
1474  border-radius:12px;
1475  position:relative;
1476  display:flex;
1477  flex-direction:column;
1478  transition:var(--transition);
1479}
1480.prod-card.small { min-width:160px; }
1481.prod-card:hover { box-shadow:0 8px 20px rgba(0,0,0,.12); transform:translateY(-3px); }
1482.prod-card .img-box {
1483  background:var(--gray-100);
1484  border-radius:10px;
1485  aspect-ratio:1/1;
1486  display:flex;
1487  align-items:center;
1488  justify-content:center;
1489  overflow:hidden;
1490}
1491.prod-card .img-box img { width:100%; height:100%; object-fit:contain; transition:var(--transition); }
1492.prod-card:hover .img-box img { transform:scale(1.04); }
1493.prod-card .name {
1494  font-size:13.5px;
1495  font-weight:500;
1496  margin:10px 0 6px;
1497  min-height:36px;
1498  line-height:1.3;
1499}
1500.price {
1501  display:flex;
1502  gap:6px;
1503  align-items:center;
1504  flex-wrap:wrap;
1505}
1506.price strong { color:var(--brand-600); font-size:14px; }
1507.price .old {
1508  font-size:11px;
1509  color:var(--gray-500);
1510  text-decoration:line-through;
1511}
1512.tag.discount {
1513  position:absolute;
1514  top:8px; left:8px;
1515  background:#E54848;
1516  color:#fff;
1517  font-size:11px;
1518  padding:4px 6px;
1519  border-radius:6px;
1520  font-weight:600;
1521}
1522.btn {
1523  border:none;
1524  cursor:pointer;
1525  font-weight:500;
1526  font-size:14px;
1527  padding:12px 22px;
1528  border-radius:10px;
1529  background:var(--gray-900);
1530  color:#fff;
1531  transition:var(--transition);
1532  text-decoration:none;
1533  text-align:center;
1534  display:inline-flex;
1535  align-items:center;
1536  justify-content:center;
1537  gap:6px;
1538}
1539.btn:hover { background:#000; }
1540.btn-primary { background:var(--brand-500); }
1541.btn-primary:hover { background:var(--brand-600); }
1542.btn-outline {
1543  background:#fff;
1544  color:var(--gray-900);
1545  border:1px solid var(--border);
1546}
1547.btn-outline:hover { background:var(--gray-100); }
1548.btn-sm { padding:8px 14px; font-size:13px; }
1549.btn-mini { padding:6px 10px; font-size:12px; }
1550.add-cart { margin-top:auto; background:var(--brand-500); color:#fff; }
1551.add-cart:hover { background:var(--brand-600); }
1552
1553/* ==== FOOTER COMPACT FIX PATCH (final) ==== */
1554
1555/* 1. Pastikan grid aktif */
1556
1557.site-footer.footer-compact .ft-grid {
1558  display:grid;
1559  grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
1560  gap:30px 40px;
1561  align-items:start;
1562}
1563
1564/* 2. Spacing atas bawah dipadatkan */
1565.site-footer.footer-compact .ft-top {
1566  padding:42px 0 26px;
1567}
1568
1569/* 3. Judul & teks */
1570.site-footer.footer-compact .ft-title {
1571  margin:0 0 14px;
1572  font-size:14px;
1573  letter-spacing:.8px;
1574  text-transform:uppercase;
1575  font-weight:600;
1576  color:#82b1c9;
1577}
1578
1579.site-footer.footer-compact .ft-desc,
1580.site-footer.footer-compact .ft-text {
1581  margin:0 0 16px;
1582  font-size:13.5px;
1583  line-height:1.45;
1584  color:#c6d6e0;
1585  max-width:330px;
1586}
1587.site-footer.footer-compact .ft-desc.sm-gap { margin-top:18px; }
1588
1589/* 4. Reset ikon SVG agar tidak membesar */
1590.site-footer.footer-compact svg {
1591  width:20px;
1592  height:20px;
1593  flex:0 0 20px;
1594  display:inline-block;
1595}
1596.site-footer.footer-compact .ft-social svg { width:18px; height:18px; }
1597.site-footer.footer-compact .ft-wa-btn svg { width:18px; height:18px; }
1598
1599/* Jika ada aturan global svg { width:100%; } — paksa override */
1600svg { max-width:100%; }
1601.site-footer.footer-compact svg {
1602  width:20px !important;
1603  height:20px !important;
1604}
1605
1606/* 5. Link list */
1607.site-footer.footer-compact .ft-links {
1608  list-style:none;
1609  margin:0 0 12px;
1610  padding:0;
1611  display:flex;
1612  flex-direction:column;
1613  gap:4px;
1614}
1615.site-footer.footer-compact .ft-links li { margin:0; }
1616.site-footer.footer-compact .ft-links a {
1617  display:flex;
1618  align-items:center;
1619  gap:8px;
1620  font-size:13.5px;
1621  padding:5px 2px;
1622  text-decoration:none;
1623  color:#c6d6e0;
1624  border-radius:8px;
1625  transition:.25s;
1626}
1627.site-footer.footer-compact .ft-links a:hover {
1628  background:rgba(0,0,0,.06);
1629  padding-left:6px;
1630  color:#fff;
1631}
1632
1633.site-footer.footer-compact .ico-s {
1634  width:24px;
1635  height:24px;
1636  background:#103346;
1637  color:#89b8cf;
1638  border-radius:6px;
1639  display:flex;
1640  align-items:center;
1641  justify-content:center;
1642  flex:0 0 24px;
1643  font-size:12px;
1644  transition:.3s;
1645}
1646.site-footer.footer-compact .ft-links a:hover .ico-s {
1647  background:#F28C1A;
1648  color:#fff;
1649  transform:rotate(-4deg);
1650}
1651
1652/* 6. WhatsApp button */
1653.site-footer.footer-compact .ft-wa-btn {
1654  display:inline-flex;
1655  align-items:center;
1656  gap:8px;
1657  background:#fff;
1658  color:#062437;
1659  padding:8px 18px;
1660  font-size:13.5px;
1661  border-radius:22px;
1662  font-weight:500;
1663  text-decoration:none;
1664  box-shadow:0 3px 8px -3px rgba(0,0,0,.45);
1665  transition:.25s;
1666}
1667.site-footer.footer-compact .ft-wa-btn:hover {
1668  transform:translateY(-2px);
1669  box-shadow:0 6px 14px -6px rgba(0,0,0,.55);
1670}
1671
1672/* 7. Sosial */
1673.site-footer.footer-compact .ft-social {
1674  display:flex;
1675  gap:12px;
1676  margin-top:4px;
1677}
1678.site-footer.footer-compact .ft-social a {
1679  width:34px;
1680  height:34px;
1681  background:rgba(0,0,0,.08);
1682  display:flex;
1683  align-items:center;
1684  justify-content:center;
1685  border-radius:10px;
1686  color:#c6d6e0;
1687  transition:.25s;
1688}
1689.site-footer.footer-compact .ft-social a:hover {
1690  background:#F28C1A;
1691  color:#fff;
1692}
1693
1694/* 8. Alamat */
1695.site-footer.footer-compact .ft-address {
1696  font-size:12.5px;
1697  line-height:1.44;
1698  color:#8fa4b1;
1699  max-width:330px;
1700  margin-top:12px;
1701}
1702.site-footer.footer-compact .ft-address .company {
1703  margin:0 0 4px;
1704  font-weight:600;
1705  color:#e5f2f7;
1706  font-size:12.8px;
1707}
1708
1709/* 9. Payments kecil & rapi */
1710.site-footer.footer-compact .ft-payments {
1711  margin-top:14px;
1712  display:flex;
1713  flex-direction:column;
1714  gap:8px;
1715  max-width:350px;
1716}
1717.site-footer.footer-compact .ft-payments .pay-row {
1718  display:flex;
1719  gap:8px;
1720  flex-wrap:wrap;
1721}
1722.site-footer.footer-compact .ft-payments img {
1723  width:46px;
1724  height:32px;
1725  padding:3px 5px;
1726  background:#fff;
1727  border-radius:7px;
1728  object-fit:contain;
1729  box-shadow:0 2px 6px -3px rgba(0,0,0,.45);
1730  transition:.25s;
1731}
1732.site-footer.footer-compact .ft-payments img:hover {
1733  transform:translateY(-3px);
1734  box-shadow:0 5px 12px -6px rgba(0,0,0,.55);
1735}
1736
1737/* 10. Bottom bar */
1738.site-footer.footer-compact .ft-bottom {
1739  border-top:1px solid rgba(0,0,0,.08);
1740  background:#041f31;
1741  padding:12px 0 16px;
1742  margin-top:4px;
1743}
1744.site-footer.footer-compact .ft-copy {
1745  font-size:11.5px;
1746  color:#8fa7b5;
1747  display:flex;
1748  flex-wrap:wrap;
1749  gap:12px;
1750  justify-content:center;
1751  line-height:1.3;
1752  margin:0;
1753}
1754.site-footer.footer-compact .ft-copy span {
1755  position:relative;
1756  padding:0 10px;
1757}
1758.site-footer.footer-compact .ft-copy span+span:before {
1759  content:"";
1760  position:absolute;
1761  left:0;
1762  top:50%;
1763  width:1px;
1764  height:12px;
1765  background:rgba(0,0,0,.14);
1766  transform:translateY(-50%);
1767}
1768@media (max-width:580px){
1769  .site-footer.footer-compact .ft-top { padding:34px 0 18px; }
1770  .site-footer.footer-compact .ft-grid { gap:26px 20px; }
1771  .site-footer.footer-compact .ft-copy span { padding:0; }
1772  .site-footer.footer-compact .ft-copy span+span:before { display:none; }
1773}
1774.copy {
1775  margin-top:40px;
1776  border-top:1px solid #222;
1777  text-align:center;
1778  padding:18px 0;
1779  font-size:12px;
1780  color:#666;
1781}
1782
1783@media (max-width:760px) {
1784  .hero-text h1 { font-size:38px; }
1785  .hero { padding:0px 0 30px; }
1786  .topbar-inner { flex-direction:column; gap:4px; padding:6px 0; }
1787  .nav-inner { flex-wrap:wrap; }
1788  .mega-cats { gap:10px; }
1789  .product-grid { gap:16px; }
1790  .prod-card { padding:10px; }
1791}
1792
1793/* === PLAZA IT DARK ORANGE FOOTER THEME === */
1794.site-footer.footer-compact {
1795  --ft-bg-0:#000000;
1796  --ft-bg-1:#000000;
1797  --ft-bg-2:#821603;
1798  --ft-bg-3:#821603;
1799  --ft-orange:#F28C1A;
1800  --ft-orange-dark:#D97809;
1801  --ft-text:#E9F2F6;
1802  --ft-text-sec:#B8CAD3;
1803  --ft-text-muted:#8FA4B1;
1804  --ft-divider:rgba(0,0,0,0.08);
1805  --ft-glow:rgba(242,140,26,0.18);
1806  --ft-glow-fade:rgba(242,140,26,0);
1807  color:var(--ft-text);
1808}
1809
1810/* BASE TEXT / LINKS (override sebelumnya jika perlu) */
1811.site-footer.footer-compact .ft-title { color:#9CC5D7; }
1812.site-footer.footer-compact .ft-links a { color:var(--ft-text-sec); }
1813.site-footer.footer-compact .ft-links a:hover { color:#fff; }
1814.site-footer.footer-compact .ft-address { color:var(--ft-text-muted); }
1815.site-footer.footer-compact .ft-copy { color:#9AB2BD; }
1816
1817/* ICON BACKGROUND (kubu gelap) */
1818.site-footer.footer-compact .ico-s {
1819  background:#0F3647;
1820  color:#9FC7DA;
1821}
1822.site-footer.footer-compact .ft-links a:hover .ico-s {
1823  background:var(--ft-orange);
1824  color:#fff;
1825}
1826
1827/* WhatsApp button biarkan putih agar kontras */
1828.site-footer.footer-compact .ft-wa-btn {
1829  box-shadow:0 4px 12px -5px rgba(0,0,0,.6), 0 0 0 3px rgba(0,0,0,0.05);
1830}
1831.site-footer.footer-compact .ft-wa-btn:hover {
1832  box-shadow:0 8px 20px -8px rgba(0,0,0,.65), 0 0 0 3px rgba(242,140,26,0.25);
1833}
1834
1835/* Payment cards border halus */
1836.site-footer.footer-compact .ft-payments img {
1837  box-shadow:0 2px 6px -3px rgba(0,0,0,.55), 0 0 0 1px rgba(0,0,0,0.05);
1838}
1839.site-footer.footer-compact .ft-payments img:hover {
1840  box-shadow:0 6px 14px -6px rgba(0,0,0,.65), 0 0 0 1px rgba(242,140,26,0.4);
1841}
1842
1843/* ========== VARIANT A: DIAGONAL EMBER (AKTIF) ========== */
1844.site-footer.footer-compact.variant-ember {
1845  background:
1846    radial-gradient(circle at 78% 82%, var(--ft-glow) 0%, var(--ft-glow-fade) 55%),
1847    linear-gradient(140deg,var(--ft-bg-1) 0%, var(--ft-bg-2) 55%, var(--ft-bg-0) 100%);
1848}
1849.site-footer.footer-compact.variant-ember .ft-bottom {
1850  background:rgba(0,0,0,0.25);
1851  backdrop-filter:blur(2px);
1852}
1853
1854/* ========== VARIANT B: TOP BAR ORANGE ========== */
1855.site-footer.footer-compact.variant-bar {
1856  background:var(--ft-bg-1);
1857  position:relative;
1858}
1859.site-footer.footer-compact.variant-bar::before {
1860  content:"";
1861  position:absolute;
1862  left:0;top:0;
1863  width:100%;height:3px;
1864  background:linear-gradient(90deg,var(--ft-orange-dark),var(--ft-orange));
1865  box-shadow:0 0 12px -2px var(--ft-orange);
1866}
1867.site-footer.footer-compact.variant-bar .ft-bottom {
1868  background:#041c28;
1869}
1870
1871/* ========== VARIANT C: RADIAL GLOW BAWAH ========== */
1872.site-footer.footer-compact.variant-radial {
1873  background:
1874    radial-gradient(circle at 70% 90%, rgba(242,140,26,0.22) 0%, rgba(242,140,26,0.04) 38%, rgba(242,140,26,0) 65%),
1875    linear-gradient(150deg,#052131 0%, #062d3f 50%, #041924 100%);
1876}
1877.site-footer.footer-compact.variant-radial .ft-bottom {
1878  background:#041c29;
1879}
1880
1881/* ========== VARIANT D: LAYERED STRIPES ========== */
1882.site-footer.footer-compact.variant-stripes {
1883  background:
1884    repeating-linear-gradient(180deg, rgba(0,0,0,0.015) 0 38px, rgba(0,0,0,0) 38px 76px),
1885    linear-gradient(130deg,#052636 0%, #062a3c 60%, #041d28 100%);
1886  position:relative;
1887}
1888.site-footer.footer-compact.variant-stripes::after {
1889  content:"";
1890  position:absolute;
1891  left:0;bottom:0;
1892  width:100%;height:110px;
1893  background:linear-gradient(0deg,rgba(0,0,0,.35),rgba(0,0,0,0));
1894  pointer-events:none;
1895}
1896.site-footer.footer-compact.variant-stripes .ft-bottom {
1897  background:#031821;
1898}
1899
1900/* Garis aksen oranye tipis di atas bottom */
1901.site-footer.footer-compact.variant-stripes .ft-bottom {
1902  position:relative;
1903}
1904.site-footer.footer-compact.variant-stripes .ft-bottom::before {
1905  content:"";
1906  position:absolute;
1907  left:0;top:0;
1908  width:100%;height:2px;
1909  background:linear-gradient(90deg,rgba(242,140,26,0),rgba(242,140,26,.65),rgba(242,140,26,0));
1910}
1911
1912/* ========== VARIANT E: GLASS PANEL ========== */
1913.site-footer.footer-compact.variant-glass {
1914  background:linear-gradient(145deg,#051c29 0%, #041620 65%, #03121b 100%);
1915}
1916.site-footer.footer-compact.variant-glass .ft-grid {
1917  position:relative;
1918  padding:32px clamp(10px,2vw,24px);
1919  border-radius:22px;
1920  background:linear-gradient(140deg,rgba(0,0,0,0.06),rgba(0,0,0,0.02));
1921  border:1px solid rgba(0,0,0,0.08);
1922  box-shadow:0 12px 28px -12px rgba(0,0,0,0.45), 0 0 0 1px rgba(0,0,0,0.04) inset;
1923  backdrop-filter:blur(8px);
1924  -webkit-backdrop-filter:blur(8px);
1925}
1926.site-footer.footer-compact.variant-glass .ft-bottom {
1927  background:#02141d;
1928}
1929
1930/* HOVER ACCENTS */
1931.site-footer.footer-compact a {
1932  transition:color .25s, background .25s, box-shadow .25s;
1933}
1934.site-footer.footer-compact .ft-links a:hover {
1935  box-shadow:0 2px 6px -3px rgba(0,0,0,.5),
1936             0 0 0 1px rgba(242,140,26,0.55);
1937}
1938.site-footer.footer-compact .ft-social a {
1939  background:#0E3445;
1940  color:#bcd4dd;
1941}
1942.site-footer.footer-compact .ft-social a:hover {
1943  background:var(--ft-orange);
1944  color:#fff;
1945  box-shadow:0 0 0 3px rgba(242,140,26,0.28);
1946}
1947
1948/* Divider di atas footer (opsional) */
1949.footer-divider-top {
1950  height:1px;
1951  background:linear-gradient(90deg,transparent,rgba(0,0,0,0.15),transparent);
1952  width:100%;
1953  margin:0 0 4px;
1954}
1955
1956/* ==== MOBILE FOOTER COLLAPSE (Non-Homepage Only) ==== */
1957
1958/* Desktop: wrapper tidak mengganggu grid layout */
1959.ft-collapsible-content {
1960  display: contents;
1961}
1962
1963@media (max-width: 768px) {
1964  /* Wrapper untuk konten yang bisa collapse */
1965  .ft-top.ft-collapsible .ft-collapsible-content {
1966    display: block;
1967    max-height: 80px;
1968    overflow: hidden;
1969    position: relative;
1970    transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
1971  }
1972  
1973  /* Fade gradient overlay */
1974  .ft-top.ft-collapsible .ft-collapsible-content::after {
1975    content: '';
1976    position: absolute;
1977    bottom: 0;
1978    left: 0;
1979    right: 0;
1980    height: 70px;
1981    background: linear-gradient(to bottom, transparent 0%, rgba(6, 43, 69, 0.6) 40%, #062b45 100%);
1982    pointer-events: none;
1983    opacity: 1;
1984    transition: opacity 0.3s ease;
1985    z-index: 1;
1986  }
1987  
1988  /* State: Expanded */
1989  .ft-top.ft-collapsible.expanded .ft-collapsible-content {
1990    max-height: 1500px;
1991  }
1992  
1993  .ft-top.ft-collapsible.expanded .ft-collapsible-content::after {
1994    opacity: 0;
1995  }
1996  
1997  /* Expand Button - Di luar collapsible, selalu visible */
1998  .ft-expand-btn {
1999    display: flex;
2000    align-items: center;
2001    justify-content: center;
2002    gap: 8px;
2003    width: 100%;
2004    margin: -8px 0 0;
2005    padding: 12px 20px;
2006    background: rgba(0,0,0,0.12);
2007    border: 1px solid rgba(0,0,0,0.25);
2008    border-radius: 10px;
2009    color: #e5f2f7;
2010    font-size: 13.5px;
2011    font-weight: 500;
2012    cursor: pointer;
2013    transition: all 0.3s ease;
2014    outline: none;
2015    position: relative;
2016    z-index: 10;
2017  }
2018  
2019  .ft-expand-btn:hover {
2020    background: rgba(0,0,0,0.18);
2021    border-color: rgba(0,0,0,0.35);
2022    color: #fff;
2023    transform: translateY(-2px);
2024  }
2025  
2026  .ft-expand-btn:active {
2027    transform: scale(0.98);
2028  }
2029  
2030  .ft-expand-btn svg {
2031    width: 18px;
2032    height: 18px;
2033    transition: transform 0.3s ease;
2034    fill: currentColor;
2035  }
2036  
2037  .ft-top.ft-collapsible.expanded .ft-expand-btn {
2038    margin-top: 15px;
2039  }
2040  
2041  .ft-top.ft-collapsible.expanded .ft-expand-btn svg {
2042    transform: rotate(180deg);
2043  }
2044}
2045
2046/* Desktop: hide expand button */
2047@media (min-width: 769px) {
2048  .ft-expand-btn {
2049    display: none !important;
2050  }
2051}
2052
2053/* RESPONSIVE (jika background panel butuh penyesuaian) */
2054@media (max-width:640px){
2055  .site-footer.footer-compact.variant-glass .ft-grid {
2056    padding:26px 18px;
2057  }
2058}
2059
2060</style>
2061  <style data-inlined="bottom-nav.css">
2062/* Mobile Bottom Navigation */
2063.bottom-nav {
2064  position: fixed;
2065  bottom: 0;
2066  left: 0;
2067  right: 0;
2068  background: #ffffff;
2069  border-top: 1px solid #e5e7eb;
2070  z-index: 9998; /* Below chat widget */
2071  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
2072  display: none; /* Hidden on desktop */
2073}
2074
2075.bottom-nav-inner {
2076  display: flex;
2077  justify-content: space-around;
2078  align-items: center;
2079  padding: 2px 0;
2080  max-width: 640px;
2081  margin: 0 auto;
2082}
2083
2084.bottom-nav-item {
2085  flex: 1;
2086  display: flex;
2087  flex-direction: column;
2088  align-items: center;
2089  justify-content: center;
2090  gap: 4px;
2091  padding: 8px 8px;
2092  background: none;
2093  border: none;
2094  cursor: pointer;
2095  text-decoration: none;
2096  color: #6b7280;
2097  transition: all 0.3s ease;
2098  position: relative;
2099}
2100
2101.bottom-nav-item:hover {
2102  color: #F28C1A;
2103}
2104
2105.bottom-nav-item.active {
2106  flex-direction: row;
2107  gap: 8px;
2108  background: linear-gradient(135deg, #F28C1A 0%, #D97809 100%);
2109  color: white;
2110  border-radius: 20px;
2111  padding: 8px 16px;
2112  box-shadow: 0 2px 8px rgba(242, 140, 26, 0.3);
2113}
2114
2115.bottom-nav-item-icon {
2116  width: 24px;
2117  height: 24px;
2118  position: relative;
2119}
2120
2121.bottom-nav-item-icon svg {
2122  width: 100%;
2123  height: 100%;
2124}
2125
2126/* Icon variant switching */
2127.bottom-nav-item .icon-outlined {
2128  display: block;
2129}
2130
2131.bottom-nav-item .icon-filled {
2132  display: none;
2133}
2134
2135.bottom-nav-item.active .icon-outlined {
2136  display: none;
2137}
2138
2139.bottom-nav-item.active .icon-filled {
2140  display: block;
2141}
2142
2143.bottom-nav-item-label {
2144  font-size: 12px;
2145  font-weight: 600;
2146  line-height: 1;
2147  display: none;
2148  white-space: nowrap;
2149}
2150
2151.bottom-nav-item.active .bottom-nav-item-label {
2152  display: block;
2153}
2154
2155/* Avatar styles for bottom-nav */
2156.bottom-nav-avatar-img,
2157.bottom-nav-avatar-initials {
2158  width: 24px;
2159  height: 24px;
2160  border-radius: 50%;
2161  display: block;
2162}
2163
2164.bottom-nav-avatar-img {
2165  object-fit: cover;
2166}
2167
2168.bottom-nav-avatar-initials {
2169  background: linear-gradient(135deg, #F28C1A 0%, #D97809 100%);
2170  color: white;
2171  font-size: 10px;
2172  font-weight: 700;
2173  display: flex;
2174  align-items: center;
2175  justify-content: center;
2176  line-height: 1;
2177  letter-spacing: 0.5px;
2178}
2179
2180/* Hide SVG when avatar is shown */
2181.bottom-nav-item-icon:has(.bottom-nav-avatar-img) .bottom-nav-svg-icon,
2182.bottom-nav-item-icon:has(.bottom-nav-avatar-initials) .bottom-nav-svg-icon {
2183  display: none;
2184}
2185
2186/* Active state adjustments for avatar */
2187.bottom-nav-item.active .bottom-nav-avatar-initials {
2188  background: white;
2189  color: #F28C1A;
2190  border: 2px solid rgba(255, 255, 255, 0.8);
2191  box-sizing: border-box;
2192}
2193
2194.bottom-nav-item.active .bottom-nav-avatar-img {
2195  border: 2px solid rgba(255, 255, 255, 0.9);
2196  box-sizing: border-box;
2197}
2198
2199.bottom-nav-item-badge {
2200  position: absolute;
2201  top: -4px;
2202  right: -4px;
2203  background: #ef4444;
2204  color: white;
2205  border-radius: 10px;
2206  padding: 2px 5px;
2207  font-size: 10px;
2208  font-weight: 600;
2209  min-width: 18px;
2210  height: 18px;
2211  display: flex;
2212  align-items: center;
2213  justify-content: center;
2214  line-height: 1;
2215}
2216
2217/* Show on mobile only */
2218@media (max-width: 640px) {
2219  .bottom-nav {
2220    display: block;
2221  }
2222  
2223  /* Add bottom padding to body to prevent content being hidden */
2224  body {
2225    padding-bottom: 60px;
2226  }
2227}
2228
2229/* Mobile User Sheet - Bottom Sheet for Profile Menu */
2230.mobile-user-sheet {
2231  position: fixed;
2232  bottom: 0;
2233  left: 0;
2234  right: 0;
2235  z-index: 10000;
2236  pointer-events: none;
2237}
2238
2239.mobile-user-sheet:not([hidden]) {
2240  pointer-events: auto;
2241}
2242
2243.mobile-user-sheet-backdrop {
2244  position: fixed;
2245  inset: 0;
2246  background: rgba(0, 0, 0, 0.5);
2247  opacity: 0;
2248  transition: opacity 0.2s ease-out;
2249}
2250
2251.mobile-user-sheet.active .mobile-user-sheet-backdrop {
2252  opacity: 1;
2253}
2254
2255.mobile-user-sheet-content {
2256  position: absolute;
2257  bottom: 0;
2258  left: 0;
2259  right: 0;
2260  background: white;
2261  border-radius: 16px 16px 0 0;
2262  max-height: 80vh;
2263  overflow-y: auto;
2264  transform: translateY(100%);
2265  transition: transform 0.3s ease-out;
2266}
2267
2268.mobile-user-sheet.active .mobile-user-sheet-content {
2269  transform: translateY(0);
2270}
2271
2272.mobile-user-sheet-handle {
2273  width: 40px;
2274  height: 4px;
2275  background: #d1d5db;
2276  border-radius: 2px;
2277  margin: 12px auto 16px;
2278}
2279
2280.mobile-user-sheet-header {
2281  padding: 0 20px 16px;
2282  border-bottom: 1px solid #e5e7eb;
2283}
2284
2285.mobile-user-info {
2286  display: flex;
2287  align-items: center;
2288  gap: 12px;
2289}
2290
2291.mobile-user-avatar {
2292  width: 48px;
2293  height: 48px;
2294  background: linear-gradient(135deg, #FFE7CC 0%, #FFD9A8 50%, #FFCC80 100%);
2295  border-radius: 50%;
2296  display: flex;
2297  align-items: center;
2298  justify-content: center;
2299  font-size: 24px;
2300  color: #D97809;
2301  font-weight: 600;
2302}
2303
2304.mobile-user-details {
2305  flex: 1;
2306}
2307
2308.mobile-user-name {
2309  font-weight: 600;
2310  font-size: 16px;
2311  color: #1f2937;
2312  margin-bottom: 4px;
2313}
2314
2315.mobile-user-email {
2316  font-size: 13px;
2317  color: #6b7280;
2318}
2319
2320.mobile-user-sheet-body {
2321  padding: 8px 0;
2322}
2323
2324.mobile-user-sheet-item {
2325  display: flex;
2326  align-items: center;
2327  gap: 12px;
2328  padding: 14px 20px;
2329  background: none;
2330  border: none;
2331  width: 100%;
2332  text-align: left;
2333  cursor: pointer;
2334  text-decoration: none;
2335  color: #374151;
2336  transition: background 0.2s;
2337}
2338
2339.mobile-user-sheet-item:hover {
2340  background: #f9fafb;
2341}
2342
2343.mobile-user-sheet-icon {
2344  font-size: 20px;
2345  width: 24px;
2346  text-align: center;
2347}
2348
2349.mobile-user-sheet-label {
2350  font-size: 15px;
2351  font-weight: 500;
2352}
2353
2354.mobile-user-sheet-divider {
2355  height: 1px;
2356  background: #e5e7eb;
2357  margin: 8px 0;
2358}
2359
2360@keyframes fadeIn {
2361  from {
2362    opacity: 0;
2363  }
2364  to {
2365    opacity: 1;
2366  }
2367}
2368
2369@keyframes slideUp {
2370  from {
2371    transform: translateY(100%);
2372  }
2373  to {
2374    transform: translateY(0);
2375  }
2376}
2377
2378/* Ensure chat widget works well with bottom nav */
2379@media (max-width: 640px) {
2380  .chat-widget {
2381    bottom: 0 !important; /* Chat opens from bottom */
2382  }
2383  
2384  .chat-popup {
2385    /* Chat popup should be above bottom nav */
2386    z-index: 9999;
2387  }
2388}
2389
2390</style>
2391  <style data-inlined="search-suggest.css">
2392.search { 
2393  position: relative; 
2394}
2395
2396/* AI Bot Toggle Button */
2397.ai-bot-toggle {
2398  display: flex;
2399  align-items: center;
2400  justify-content: center;
2401  background: transparent;
2402  border: none;
2403  padding: 8px;
2404  cursor: pointer;
2405  border-radius: 8px;
2406  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2407  margin-left: 4px;
2408  flex-shrink: 0;
2409  display: none;
2410}
2411
2412.ai-bot-toggle .bot-icon {
2413  color: #9ca3af;
2414  transition: all 0.3s ease;
2415}
2416
2417.ai-bot-toggle:hover .bot-icon {
2418  color: #6b7280;
2419  transform: scale(1.1);
2420}
2421
2422.ai-bot-toggle.active .bot-icon {
2423  color: #fb923c;
2424  animation: botPulse 2s ease-in-out infinite;
2425}
2426
2427.ai-bot-toggle.active {
2428  background: rgba(251, 146, 60, 0.1);
2429}
2430
2431.ai-bot-toggle.active:hover .bot-icon {
2432  color: #f97316;
2433}
2434
2435@keyframes botPulse {
2436  0%, 100% { transform: scale(1); }
2437  50% { transform: scale(1.12); }
2438}
2439
2440/* Search Suggest Box */
2441.search-suggest {
2442  position: absolute;
2443  top: 100%; left: 0; right: 0;
2444  background: #fff;
2445  border: 1px solid #e1e4e8;
2446  border-top: none;
2447  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
2448  max-height: 400px;
2449  overflow-y: auto;
2450  z-index: 100;
2451  padding: 0;
2452  font-size: 14px;
2453  border-radius: 0 0 8px 8px;
2454}
2455
2456.suggest-item {
2457  display: flex;
2458  align-items: center;
2459  gap: 8px;
2460  padding: 10px 16px;
2461  cursor: pointer;
2462  transition: all 0.2s ease;
2463  border-bottom: 1px solid #f1f5f9;
2464}
2465
2466.suggest-item:last-child {
2467  border-bottom: none;
2468}
2469
2470.suggest-item:hover,
2471.suggest-item.active {
2472  background: linear-gradient(90deg, #f8fafc 0%, #f1f5f9 100%);
2473  padding-left: 20px;
2474}
2475
2476.suggest-thumb {
2477  width: 42px;
2478  height: 42px;
2479  object-fit: cover;
2480  border: 1px solid #ddd;
2481  border-radius: 4px;
2482  background: #fafafa;
2483}
2484
2485.suggest-meta { flex: 1; min-width: 0; }
2486
2487.suggest-name {
2488  font-weight: 500;
2489  color: #222;
2490  line-height: 1.2;
2491  white-space: nowrap;
2492  overflow: hidden;
2493  text-overflow: ellipsis;
2494}
2495
2496.suggest-price {
2497  color: #d32f2f;
2498  font-size: 12px;
2499  margin-top: 2px;
2500}
2501
2502/* AI Insights */
2503.suggest-ai-insight {
2504  display: flex;
2505  align-items: center;
2506  gap: 8px;
2507  padding: 8px 12px;
2508  background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
2509  border-bottom: 1px solid #bae6fd;
2510  color: #0369a1;
2511  font-size: 12px;
2512  font-weight: 500;
2513  line-height: 1.4;
2514}
2515
2516.suggest-ai-insight svg {
2517  flex-shrink: 0;
2518  opacity: 0.8;
2519}
2520
2521.suggest-ai-insight span {
2522  flex: 1;
2523}
2524
2525/* AI Progress States */
2526.ai-progress-container {
2527  padding: 20px 16px;
2528  background: linear-gradient(135deg, #fafbff 0%, #f5f7ff 100%);
2529}
2530
2531.ai-progress-header {
2532  display: flex;
2533  align-items: center;
2534  gap: 10px;
2535  margin-bottom: 16px;
2536}
2537
2538.ai-progress-icon {
2539  width: 36px;
2540  height: 36px;
2541  display: flex;
2542  align-items: center;
2543  justify-content: center;
2544  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
2545  border-radius: 50%;
2546  color: #fff;
2547  font-size: 18px;
2548  animation: aiIconSpin 3s linear infinite;
2549}
2550
2551@keyframes aiIconSpin {
2552  0% { transform: rotate(0deg); }
2553  100% { transform: rotate(360deg); }
2554}
2555
2556.ai-progress-text {
2557  flex: 1;
2558}
2559
2560.ai-progress-title {
2561  font-weight: 600;
2562  color: #1e293b;
2563  font-size: 14px;
2564  margin-bottom: 2px;
2565}
2566
2567.ai-progress-subtitle {
2568  font-size: 12px;
2569  color: #64748b;
2570}
2571
2572/* Progress Steps */
2573.ai-progress-steps {
2574  display: flex;
2575  flex-direction: column;
2576  gap: 10px;
2577  margin-top: 16px;
2578}
2579
2580.ai-progress-step {
2581  display: flex;
2582  align-items: center;
2583  gap: 10px;
2584  padding: 8px 12px;
2585  background: #fff;
2586  border-radius: 8px;
2587  border: 1px solid #e2e8f0;
2588  transition: all 0.3s ease;
2589}
2590
2591.ai-progress-step.active {
2592  border-color: #667eea;
2593  background: #f8f9ff;
2594  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
2595}
2596
2597.ai-progress-step.completed {
2598  border-color: #10b981;
2599  background: #f0fdf4;
2600}
2601
2602.ai-step-icon {
2603  width: 20px;
2604  height: 20px;
2605  display: flex;
2606  align-items: center;
2607  justify-content: center;
2608  border-radius: 50%;
2609  font-size: 11px;
2610  flex-shrink: 0;
2611}
2612
2613.ai-progress-step.active .ai-step-icon {
2614  background: #667eea;
2615  color: #fff;
2616  animation: pulse 1.5s ease-in-out infinite;
2617}
2618
2619.ai-progress-step.completed .ai-step-icon {
2620  background: #10b981;
2621  color: #fff;
2622}
2623
2624.ai-progress-step.pending .ai-step-icon {
2625  background: #e2e8f0;
2626  color: #94a3b8;
2627}
2628
2629.ai-step-text {
2630  flex: 1;
2631  font-size: 13px;
2632  color: #475569;
2633  font-weight: 500;
2634}
2635
2636.ai-progress-step.completed .ai-step-text {
2637  color: #059669;
2638}
2639
2640/* AI Analysis Result Banner */
2641.ai-result-banner {
2642  padding: 12px 16px;
2643  background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
2644  border-bottom: 1px solid #86efac;
2645  display: flex;
2646  align-items: center;
2647  gap: 10px;
2648}
2649
2650.ai-result-icon {
2651  font-size: 20px;
2652  animation: bounceIn 0.5s ease;
2653}
2654
2655@keyframes bounceIn {
2656  0% { transform: scale(0); opacity: 0; }
2657  50% { transform: scale(1.1); }
2658  100% { transform: scale(1); opacity: 1; }
2659}
2660
2661.ai-result-text {
2662  flex: 1;
2663}
2664
2665.ai-result-title {
2666  font-weight: 600;
2667  color: #065f46;
2668  font-size: 13px;
2669  margin-bottom: 2px;
2670}
2671
2672.ai-result-description {
2673  font-size: 12px;
2674  color: #047857;
2675  opacity: 0.9;
2676}
2677
2678/* Product Items Section Header */
2679.suggest-section-header {
2680  padding: 10px 16px;
2681  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
2682  border-bottom: 1px solid #e2e8f0;
2683}
2684
2685.suggest-header-main {
2686  display: flex;
2687  align-items: center;
2688  gap: 6px;
2689  margin-bottom: 4px;
2690}
2691
2692.suggest-header-icon {
2693  font-size: 14px;
2694}
2695
2696.suggest-header-text {
2697  font-size: 12px;
2698  font-weight: 600;
2699  color: #64748b;
2700  text-transform: uppercase;
2701  letter-spacing: 0.5px;
2702}
2703
2704.suggest-header-hint {
2705  font-size: 11px;
2706  color: #94a3b8;
2707  font-weight: 400;
2708  text-transform: none;
2709  letter-spacing: 0;
2710  display: flex;
2711  align-items: center;
2712  gap: 4px;
2713}
2714
2715.suggest-header-hint kbd {
2716  display: inline-block;
2717  padding: 2px 6px;
2718  font-size: 10px;
2719  font-weight: 600;
2720  line-height: 1;
2721  color: #475569;
2722  background: #fff;
2723  border: 1px solid #cbd5e1;
2724  border-radius: 4px;
2725  box-shadow: 0 1px 2px rgba(0,0,0,0.05);
2726  font-family: 'Segoe UI', monospace;
2727}
2728
2729/* Footer Hint */
2730.suggest-footer-hint {
2731  display: flex;
2732  align-items: center;
2733  justify-content: center;
2734  padding: 12px 16px;
2735  background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
2736  border-top: 1px solid #fbbf24;
2737  color: #92400e;
2738  font-size: 12px;
2739  font-weight: 500;
2740  text-align: center;
2741  cursor: default;
2742}
2743
2744.suggest-footer-hint svg {
2745  color: #f59e0b;
2746  flex-shrink: 0;
2747}
2748
2749.suggest-footer-hint kbd {
2750  display: inline-block;
2751  padding: 3px 7px;
2752  margin: 0 3px;
2753  font-size: 11px;
2754  font-weight: 600;
2755  line-height: 1;
2756  color: #78350f;
2757  background: #fff;
2758  border: 1px solid #f59e0b;
2759  border-radius: 4px;
2760  box-shadow: 0 1px 3px rgba(245, 158, 11, 0.2);
2761  font-family: 'Segoe UI', monospace;
2762}
2763
2764.suggest-footer-link {
2765  color: #ea580c;
2766  font-weight: 600;
2767  text-decoration: underline;
2768  text-underline-offset: 2px;
2769  text-decoration-thickness: 1px;
2770  cursor: pointer;
2771  transition: all 0.2s ease;
2772}
2773
2774.suggest-footer-link:hover {
2775  color: #c2410c;
2776  text-decoration-thickness: 2px;
2777  text-shadow: 0 0 8px rgba(234, 88, 12, 0.3);
2778}
2779
2780/* Loading state */
2781.suggest-loading {
2782  display: flex;
2783  align-items: center;
2784  justify-content: center;
2785  gap: 8px;
2786  padding: 16px 12px;
2787  color: #64748b;
2788  font-size: 13px;
2789}
2790
2791@keyframes pulse {
2792  0%, 100% { opacity: 1; }
2793  50% { opacity: 0.5; }
2794}
2795
2796.suggest-loading {
2797  animation: pulse 1.5s ease-in-out infinite;
2798}
2799
2800/* Responsive Mobile Styles */
2801@media (max-width: 768px) {
2802  .ai-bot-toggle {
2803    padding: 6px;
2804    margin-left: 0;
2805  }
2806  
2807  .ai-bot-toggle .bot-icon {
2808    width: 20px;
2809    height: 20px;
2810  }
2811  
2812  .search-icon-btn {
2813    margin-right: 4px;
2814  }
2815  
2816  .search-icon-btn svg {
2817    width: 18px;
2818    height: 18px;
2819  }
2820  
2821  .search-suggest {
2822    max-height: 350px;
2823  }
2824  
2825  /* Mobile hint adjustments */
2826  .suggest-header-hint {
2827    font-size: 10px;
2828  }
2829  
2830  .suggest-header-hint kbd {
2831    padding: 1px 4px;
2832    font-size: 9px;
2833  }
2834  
2835  .suggest-footer-hint {
2836    padding: 10px 12px;
2837    font-size: 11px;
2838  }
2839  
2840  .suggest-footer-hint svg {
2841    width: 14px;
2842    height: 14px;
2843  }
2844  
2845  .suggest-footer-hint kbd {
2846    padding: 2px 5px;
2847    font-size: 10px;
2848  }
2849  
2850  .ai-progress-container {
2851    padding: 16px 12px;
2852  }
2853  
2854  .ai-progress-icon {
2855    width: 32px;
2856    height: 32px;
2857    font-size: 16px;
2858  }
2859  
2860  .ai-progress-title {
2861    font-size: 13px;
2862  }
2863  
2864  .ai-progress-subtitle {
2865    font-size: 11px;
2866  }
2867  
2868  .ai-progress-step {
2869    padding: 6px 10px;
2870  }
2871  
2872  .ai-step-text {
2873    font-size: 12px;
2874  }
2875  
2876  .suggest-item {
2877    padding: 8px 12px;
2878  }
2879  
2880  .suggest-thumb {
2881    width: 36px;
2882    height: 36px;
2883  }
2884  
2885  .suggest-name {
2886    font-size: 13px;
2887  }
2888  
2889  .suggest-price {
2890    font-size: 11px;
2891  }
2892}
2893
2894@media (max-width: 480px) {
2895  .ai-bot-toggle {
2896    padding: 4px;
2897  }
2898  
2899  .ai-bot-toggle .bot-icon {
2900    width: 18px;
2901    height: 18px;
2902  }
2903  
2904  .search-icon-btn svg {
2905    width: 16px;
2906    height: 16px;
2907  }
2908  
2909  /* Ultra-compact hints for small screens */
2910  .suggest-header-hint {
2911    display: none; /* Hide header hint on very small screens */
2912  }
2913  
2914  .suggest-footer-hint {
2915    padding: 8px 10px;
2916    font-size: 10px;
2917    flex-direction: column;
2918    gap: 4px;
2919  }
2920  
2921  .suggest-footer-hint svg {
2922    display: none; /* Hide icon to save space */
2923  }
2924  
2925  .suggest-footer-hint kbd {
2926    padding: 2px 4px;
2927    font-size: 9px;
2928  }
2929  
2930  .ai-progress-steps {
2931    gap: 8px;
2932  }
2933  
2934  .ai-step-text {
2935    font-size: 11px;
2936  }
2937}
2938
2939/* AI Search Prompt Box */
2940.ai-search-prompt {
2941  padding: 24px;
2942  background: linear-gradient(135deg, #fff5f0 0%, #ffffff 100%);
2943}
2944
2945.ai-prompt-header {
2946  display: flex;
2947  align-items: center;
2948  gap: 12px;
2949  margin-bottom: 16px;
2950}
2951
2952.ai-prompt-icon {
2953  width: 48px;
2954  height: 48px;
2955  background: linear-gradient(135deg, #fb923c 0%, #f97316 100%);
2956  border-radius: 12px;
2957  display: flex;
2958  align-items: center;
2959  justify-content: center;
2960  font-size: 24px;
2961  box-shadow: 0 4px 12px rgba(251, 146, 60, 0.3);
2962}
2963
2964.ai-prompt-text {
2965  flex: 1;
2966}
2967
2968.ai-prompt-title {
2969  font-size: 16px;
2970  font-weight: 700;
2971  color: #1f2937;
2972  margin-bottom: 4px;
2973}
2974
2975.ai-prompt-subtitle {
2976  font-size: 13px;
2977  color: #6b7280;
2978}
2979
2980.ai-prompt-query {
2981  background: white;
2982  border: 2px solid #fb923c;
2983  border-radius: 8px;
2984  padding: 12px 16px;
2985  margin-bottom: 16px;
2986}
2987
2988.ai-query-label {
2989  font-size: 11px;
2990  font-weight: 600;
2991  color: #fb923c;
2992  text-transform: uppercase;
2993  letter-spacing: 0.5px;
2994  margin-bottom: 4px;
2995}
2996
2997.ai-query-text {
2998  font-size: 15px;
2999  font-weight: 600;
3000  color: #1f2937;
3001}
3002
3003.ai-search-trigger-btn {
3004  width: 100%;
3005  padding: 14px 20px;
3006  background: linear-gradient(135deg, #fb923c 0%, #f97316 100%);
3007  color: white;
3008  border: none;
3009  border-radius: 10px;
3010  font-size: 15px;
3011  font-weight: 700;
3012  cursor: pointer;
3013  display: flex;
3014  align-items: center;
3015  justify-content: center;
3016  gap: 8px;
3017  transition: all 0.3s ease;
3018  box-shadow: 0 4px 12px rgba(251, 146, 60, 0.4);
3019}
3020
3021.ai-search-trigger-btn:hover {
3022  transform: translateY(-2px);
3023  box-shadow: 0 6px 20px rgba(251, 146, 60, 0.5);
3024}
3025
3026.ai-search-trigger-btn:active {
3027  transform: translateY(0);
3028}
3029
3030.ai-btn-icon {
3031  font-size: 18px;
3032  animation: sparkle 2s ease-in-out infinite;
3033}
3034
3035@keyframes sparkle {
3036  0%, 100% { opacity: 1; transform: scale(1); }
3037  50% { opacity: 0.7; transform: scale(1.2); }
3038}
3039
3040.ai-btn-text {
3041  font-size: 15px;
3042}
3043
3044.ai-prompt-footer {
3045  margin-top: 12px;
3046  text-align: center;
3047  font-size: 12px;
3048  color: #9ca3af;
3049  font-style: italic;
3050}
3051
3052/* ==================== TRENDING SEARCH STYLES ==================== */
3053
3054.search-suggest.trending-mode {
3055  border-top: 3px solid #fb923c;
3056  max-width: 900px;
3057  min-width: 600px;
3058}
3059
3060/* Two-column container */
3061.trending-container {
3062  display: flex;
3063  gap: 0;
3064  padding: 0 !important;
3065}
3066
3067.trending-column {
3068  flex: 1;
3069  min-width: 0;
3070  padding: 0 !important;
3071}
3072
3073.trending-column.trending-searches {
3074  border-right: 2px solid #e2e8f0; padding: 0 !important;
3075}
3076
3077/* Mobile: stack columns vertically */
3078@media (max-width: 768px) {
3079  .search-suggest.trending-mode {
3080    min-width: auto;
3081    max-width: 100%;
3082  }
3083  
3084  .trending-container {
3085    flex-direction: column;
3086  }
3087  
3088  .trending-column.trending-searches {
3089    border-right: none;
3090    border-bottom: 2px solid #e2e8f0;
3091  }
3092}
3093
3094.trending-header {
3095  display: flex;
3096  align-items: center;
3097  gap: 8px;
3098  padding: 14px 16px;
3099  border-bottom: 1px solid #e2e8f0;
3100  background: linear-gradient(135deg, #fb923c 0%, #f97316 100%);
3101  color: white;
3102  position: sticky;
3103  top: 0;
3104  z-index: 10;
3105}
3106
3107.trending-icon {
3108  font-size: 20px;
3109  animation: trendingPulse 2s infinite;
3110}
3111
3112@keyframes trendingPulse {
3113  0%, 100% { 
3114    transform: scale(1); 
3115    filter: drop-shadow(0 0 0 rgba(251, 146, 60, 0));
3116  }
3117  50% { 
3118    transform: scale(1.15); 
3119    filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.8));
3120  }
3121}
3122
3123.trending-title {
3124  font-weight: 700;
3125  font-size: 14px;
3126  letter-spacing: 0.3px;
3127}
3128
3129.trending-subtitle {
3130  font-size: 11px;
3131  opacity: 0.9;
3132  margin-left: auto;
3133  font-style: italic;
3134}
3135
3136/* Trending searches item */
3137.trending-item {
3138  display: flex;
3139  align-items: center;
3140  gap: 12px;
3141  padding: 12px 16px;
3142  cursor: pointer;
3143  transition: all 0.2s ease;
3144  border-bottom: 1px solid #f1f5f9;
3145  position: relative;
3146}
3147
3148.trending-item:hover {
3149  background: linear-gradient(90deg, #fff7ed 0%, #ffedd5 100%);
3150  padding-left: 20px;
3151}
3152
3153.trending-item:hover::before {
3154  content: '';
3155  position: absolute;
3156  left: 0;
3157  top: 0;
3158  bottom: 0;
3159  width: 4px;
3160  background: linear-gradient(180deg, #fb923c 0%, #f97316 100%);
3161}
3162
3163.trending-item:last-child {
3164  border-bottom: none;
3165}
3166
3167.trending-rank {
3168  font-size: 20px;
3169  flex-shrink: 0;
3170  width: 32px;
3171  text-align: center;
3172}
3173
3174.trending-query {
3175  flex: 1;
3176  font-size: 14px;
3177  color: #1f2937;
3178  font-weight: 500;
3179  line-height: 1.4;
3180}
3181
3182.trending-category {
3183  font-size: 11px;
3184  color: #f97316;
3185  background: #fff7ed;
3186  padding: 3px 10px;
3187  border-radius: 12px;
3188  white-space: nowrap;
3189  font-weight: 500;
3190  border: 1px solid #fed7aa;
3191}
3192
3193/* Trending products item */
3194.trending-product-item {
3195  display: flex;
3196  align-items: center;
3197  gap: 12px;
3198  padding: 12px 16px;
3199  cursor: pointer;
3200  transition: all 0.2s ease;
3201  border-bottom: 1px solid #f1f5f9;
3202  position: relative;
3203}
3204
3205.trending-product-item:hover {
3206  background: linear-gradient(90deg, #fef3c7 0%, #fde68a 100%);
3207  padding-left: 20px;
3208}
3209
3210.trending-product-item:hover::before {
3211  content: '';
3212  position: absolute;
3213  left: 0;
3214  top: 0;
3215  bottom: 0;
3216  width: 4px;
3217  background: linear-gradient(180deg, #fbbf24 0%, #f59e0b 100%);
3218}
3219
3220.trending-product-item:last-child {
3221  border-bottom: none;
3222}
3223
3224.trending-product-thumb {
3225  width: 48px;
3226  height: 48px;
3227  object-fit: cover;
3228  border-radius: 8px;
3229  border: 1px solid #e5e7eb;
3230  flex-shrink: 0;
3231  background: white;
3232}
3233
3234.trending-product-info {
3235  flex: 1;
3236  min-width: 0;
3237}
3238
3239.trending-product-name {
3240  font-size: 13px;
3241  font-weight: 400;
3242  color: #1f2937;
3243  line-height: 1.4;
3244  margin-bottom: 0;
3245  padding: 0 !important;
3246  overflow: hidden;
3247  text-overflow: ellipsis;
3248  display: -webkit-box;
3249  -webkit-line-clamp: 2;
3250  -webkit-box-orient: vertical;
3251}
3252
3253.trending-product-price {
3254  font-size: 12px;
3255  font-weight: 700;
3256  color: #f59e0b;
3257  padding: 4px 0 !important;
3258}
3259.trending-product { padding: 0;}
3260/* ==================== END TRENDING STYLES ==================== */
3261
3262</style>
3263  <style data-inlined="product-detail.css">
3264/* === PRODUCT DETAIL v3 (Tokopedia-like) — Full stylesheet === */
3265
3266/* Root / Container */
3267.product-single {
3268  --pd-gap: 2rem;
3269  --pd-border: #e2e8f0;
3270  --pd-bg: #ffffff;
3271  --pd-muted: #64748b;
3272  --pd-dark: #0f172a;
3273  --pd-accent: #0d6efd; /* tema utama situs */
3274  --pd-radius: 12px;
3275  --pd-soft: #f8fafc;
3276  --pd-soft-border: #e5e7eb;
3277  --pd-danger: #dc2626;
3278  --pd-success-bg: #dcfce7;
3279  --pd-success-text: #166534;
3280  --pd-danger-bg: #fee2e2;
3281  --pd-danger-text: #b91c1c;
3282  --pd-shadow: 0 6px 20px -10px rgba(0,0,0,.12);
3283  --pd-transition: .25s cubic-bezier(.4,.0,.2,1);
3284  max-width:1280px;
3285  margin-inline:auto;
3286}
3287
3288/* === Sticky Product Header Bar (muncul saat scroll) === */
3289.pd-sticky-header {
3290  position: fixed;
3291  top: 0;
3292  left: 0;
3293  right: 0;
3294  z-index: 998;
3295  background: rgba(255, 255, 255, 0.98);
3296  backdrop-filter: blur(12px);
3297  -webkit-backdrop-filter: blur(12px);
3298  border-bottom: 1px solid rgba(226, 232, 240, 0.8);
3299  box-shadow: 0 4px 16px -4px rgba(0, 0, 0, 0.08);
3300  transform: translateY(-100%);
3301  opacity: 0;
3302  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), 
3303              opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
3304  pointer-events: none;
3305}
3306
3307.pd-sticky-header.visible {
3308  transform: translateY(0);
3309  opacity: 1;
3310  pointer-events: auto;
3311}
3312
3313.pd-sticky-header-container {
3314  max-width: 1280px;
3315  margin: 0 auto;
3316  padding: 12px 20px;
3317  display: flex;
3318  align-items: center;
3319  gap: 16px;
3320}
3321
3322.pd-sticky-thumb {
3323  width: 48px;
3324  height: 48px;
3325  flex-shrink: 0;
3326  border-radius: 8px;
3327  border: 1px solid #e2e8f0;
3328  overflow: hidden;
3329  background: #f8fafc;
3330}
3331
3332.pd-sticky-thumb img {
3333  width: 100%;
3334  height: 100%;
3335  object-fit: cover;
3336  display: block;
3337}
3338
3339.pd-sticky-info {
3340  flex: 1;
3341  min-width: 0;
3342  display: flex;
3343  flex-direction: column;
3344  gap: 4px;
3345}
3346
3347.pd-sticky-title {
3348  font-size: 0.9rem;
3349  font-weight: 600;
3350  color: #0f172a;
3351  line-height: 1.3;
3352  white-space: nowrap;
3353  overflow: hidden;
3354  text-overflow: ellipsis;
3355  margin: 0;
3356}
3357
3358.pd-sticky-price-row {
3359  display: flex;
3360  align-items: center;
3361  gap: 8px;
3362  flex-wrap: wrap;
3363}
3364
3365.pd-sticky-price {
3366  font-size: 1.1rem;
3367  font-weight: 700;
3368  color: #0d6efd;
3369  line-height: 1;
3370}
3371
3372.pd-sticky-price-original {
3373  font-size: 0.75rem;
3374  color: #94a3b8;
3375  text-decoration: line-through;
3376  line-height: 1;
3377}
3378
3379.pd-sticky-discount {
3380  background: #dc2626;
3381  color: #fff;
3382  font-size: 0.65rem;
3383  font-weight: 600;
3384  padding: 2px 6px;
3385  border-radius: 4px;
3386  letter-spacing: 0.3px;
3387  line-height: 1;
3388}
3389
3390.pd-sticky-actions {
3391  display: flex;
3392  gap: 8px;
3393  flex-shrink: 0;
3394  align-items: center;
3395}
3396
3397.pd-sticky-share-btn {
3398  padding: 8px 16px;
3399  border-radius: 8px;
3400  border: 1px solid #e2e8f0;
3401  background: #fff;
3402  color: #64748b;
3403  cursor: pointer;
3404  transition: all 0.2s ease;
3405  display: inline-flex;
3406  align-items: center;
3407  justify-content: center;
3408  flex-shrink: 0;
3409}
3410
3411.pd-sticky-share-btn:hover {
3412  background: #f8fafc;
3413  border-color: #cbd5e1;
3414  color: #0f172a;
3415  transform: translateY(-1px);
3416}
3417
3418.pd-sticky-share-btn:active {
3419  transform: translateY(0);
3420}
3421
3422.pd-sticky-share-btn svg {
3423  display: block;
3424  width: 16px;
3425  height: 16px;
3426}
3427
3428.pd-sticky-btn-chat svg {
3429  width: 16px !important;
3430  height: 16px !important;
3431  max-width: none !important;
3432  margin-right: 4px;
3433  flex-shrink: 0;
3434}
3435
3436.pd-sticky-btn {
3437  padding: 8px 16px;
3438  border-radius: 8px;
3439  font-size: 0.85rem;
3440  font-weight: 600;
3441  cursor: pointer;
3442  transition: all 0.2s ease;
3443  border: 0;
3444  display: inline-flex;
3445  align-items: center;
3446  justify-content: center;
3447  white-space: nowrap;
3448}
3449
3450.pd-sticky-btn-cart {
3451  background: #fff;
3452  color: #0d6efd;
3453  border: 1px solid #0d6efd;
3454}
3455
3456.pd-sticky-btn-cart:hover {
3457  background: rgba(13, 110, 253, 0.06);
3458}
3459
3460.pd-sticky-btn-chat {
3461  background: #fff;
3462  color: #64748b;
3463  border: 1px solid #cbd5e1;
3464  flex: 0.8;
3465}
3466
3467.pd-sticky-btn-chat:hover {
3468  background: #f8fafc;
3469  border-color: #94a3b8;
3470  color: #475569;
3471}
3472
3473.pd-sticky-btn-buy {
3474  background: #0d6efd;
3475  color: #fff;
3476  border: 1px solid #0d6efd;
3477  box-shadow: 0 2px 8px -2px rgba(13, 110, 253, 0.4);
3478}
3479
3480.pd-sticky-btn-buy:hover {
3481  filter: brightness(0.95);
3482}
3483
3484.pd-sticky-btn:disabled {
3485  opacity: 0.5;
3486  cursor: not-allowed;
3487}
3488
3489/* Hide on mobile (use existing FAB instead) */
3490@media (max-width: 880px) {
3491  .pd-sticky-header {
3492    display: none;
3493  }
3494}
3495
3496/* === PRINT STYLES (for PDF Export) === */
3497@media print {
3498  /* Reset & Base */
3499  * {
3500    -webkit-print-color-adjust: exact !important;
3501    print-color-adjust: exact !important;
3502  }
3503  
3504  body {
3505    background: white !important;
3506    margin: 0;
3507    padding: 0;
3508  }
3509  
3510  /* Hide all navigation, buttons, and interactive elements */
3511  nav, header, footer,
3512  nav *, header *, footer *,
3513  .navbar, .navbar *,
3514  .nav-item, .nav-link,
3515  .pd-sticky-header,
3516  .pd-sticky-header.visible,
3517  .pd-breadcrumb,
3518  .pd-sidebar,
3519  .pd-mobile-fab,
3520  .pd-share-btn,
3521  .pd-sticky-share-btn,
3522  #pd-share-btn,
3523  .share-button,
3524  [data-share-btn],
3525  button,
3526  .btn-add-cart,
3527  .btn-buy,
3528  .pd-actions,
3529  .pd-compare-section,
3530  .pd-compare-btn,
3531  .pd-compare-info,
3532  #pd-compare-btn,
3533  .compare-note,
3534  .compare-container,
3535  .compare-dropdown,
3536  .compare-panel,
3537  .compare-wrapper,
3538  [data-compare],
3539  [id*="compare"],
3540  [class*="compare"],
3541  .pd-thumb-gallery,
3542  .pd-installment-card,
3543  .pd-installment-card2,
3544  .inst-alt-banner--compact,
3545  .qoala-banner-card,
3546  .tradein-compact-card,
3547  .pwp-deals-section,
3548  #pwp-deals-section,
3549  .pwp-trigger-badge,
3550  .pd-related-full,
3551  #pd-similar,
3552  .pd-similar-strip,
3553  .pd-scroll-top,
3554  .stock-info-btn,
3555  .stock-popover,
3556  .pd-countdown-modern,
3557  .pd-share-modal-overlay,
3558  [id*="promo-"],
3559  .bottom-nav,
3560  .pd-buy-form,
3561  .pd-buy-card,
3562  .pd-extra-meta,
3563  .pd-reviews-section,
3564  #reviews,
3565  hr,
3566  .divider,
3567  .separator {
3568    display: none !important;
3569    visibility: hidden !important;
3570    opacity: 0 !important;
3571    height: 0 !important;
3572    max-height: 0 !important;
3573    overflow: hidden !important;
3574    margin: 0 !important;
3575    padding: 0 !important;
3576    border: none !important;
3577  }
3578  
3579  /* Extra aggressive reset for any remaining borders */
3580  .pd-info-card *,
3581  .pd-meta-inline *,
3582  .pd-variants * {
3583    border: none !important;
3584    border-top: none !important;
3585    border-bottom: none !important;
3586  }
3587  
3588  /* Layout restructure */
3589  .product-single {
3590    max-width: 100% !important;
3591    margin: 0 !important;
3592    padding: 0 !important;
3593    box-shadow: none !important;
3594  }
3595  
3596  /* Reset all shadows and borders for print */
3597  * {
3598    box-shadow: none !important;
3599  }
3600  
3601  .pd-top-layout {
3602    display: grid !important;
3603    grid-template-columns: 280px 1fr !important;
3604    gap: 24px !important;
3605    margin: 0 0 24px 0 !important;
3606    padding: 0 !important;
3607    page-break-inside: avoid;
3608    align-items: start !important;
3609  }
3610  
3611  /* Print header with logo - centered and larger */
3612  .product-single::before {
3613    content: "" !important;
3614    display: block !important;
3615    width: 150px !important;
3616    height: 50px !important;
3617    background: url('/assets/img/logo.png') no-repeat center !important;
3618    background-size: contain !important;
3619    margin: 0 auto 20px !important;
3620    visibility: visible !important;
3621    opacity: 1 !important;
3622  }
3623  
3624  /* Media card - left column */
3625  .pd-media-card {
3626    border: none !important;
3627    padding: 0 !important;
3628    margin: 0 !important;
3629    text-align: center;
3630    page-break-inside: avoid;
3631  }
3632  
3633  /* Hide desktop image, use mobile slider for both desktop and mobile */
3634  .pd-main-image {
3635    display: none !important;
3636  }
3637  
3638  /* Mobile slider - show first image only in print */
3639  .pd-mobile-slider {
3640    display: block !important;
3641    overflow: visible !important;
3642  }
3643  
3644  .pd-mslide {
3645    display: none !important;
3646  }
3647  
3648  .pd-mslide:first-child {
3649    display: block !important;
3650    border: 1px solid #e2e8f0 !important;
3651    max-width: 280px !important;
3652    margin: 0 auto !important;
3653    border-radius: 8px !important;
3654  }
3655  
3656  .pd-mslide:first-child img {
3657    max-height: 280px !important;
3658    width: 100% !important;
3659    height: auto !important;
3660    object-fit: contain !important;
3661  }
3662  
3663  /* Info card - right column */
3664  .pd-info-card {
3665    border: none !important;
3666    border-top: none !important;
3667    border-bottom: none !important;
3668    padding: 0 !important;
3669    margin: 0 !important;
3670    background: white !important;
3671    box-shadow: none !important;
3672  }
3673  
3674  /* Title */
3675  .pd-title-row {
3676    display: block !important;
3677    margin-bottom: 10px !important;
3678    border: none !important;
3679    border-top: none !important;
3680    border-bottom: none !important;
3681    padding: 0 !important;
3682    box-shadow: none !important;
3683    text-decoration: none !important;
3684  }
3685  
3686  /* Hide share button in print */
3687  .pd-title-row .pd-share-btn,
3688  .pd-title-row #pd-share-btn {
3689    display: none !important;
3690  }
3691  
3692  .pd-title {
3693    font-size: 13pt !important;
3694    color: #000 !important;
3695    margin: 0 0 10px 0 !important;
3696    page-break-after: avoid;
3697    font-weight: 700 !important;
3698    line-height: 1.3 !important;
3699    border: none !important;
3700    border-top: none !important;
3701    border-bottom: none !important;
3702    padding-bottom: 0 !important;
3703    box-shadow: none !important;
3704    text-decoration: none !important;
3705  }
3706  
3707  /* Price and QR container - side by side */
3708  .pd-price-qr-container {
3709    display: flex !important;
3710    gap: 16px !important;
3711    align-items: flex-start !important;
3712    margin-bottom: 14px !important;
3713    box-shadow: none !important;
3714    border: none !important;
3715    text-decoration: none !important;
3716  }
3717  
3718  /* Price block - left side */
3719  .pd-price-block {
3720    flex: 1 !important;
3721    margin: 0 !important;
3722    padding-bottom: 0 !important;
3723    border-bottom: none !important;
3724    box-shadow: none !important;
3725  }
3726  
3727  .pd-price-row {
3728    margin-bottom: 8px !important;
3729    border: none !important;
3730    border-top: none !important;
3731    border-bottom: none !important;
3732    padding: 0 !important;
3733  }
3734  
3735  /* Hide all empty or unnecessary elements in price row */
3736  .pd-price-row > span:empty,
3737  .pd-price-row > .sold-inline,
3738  #pd-sold-inline {
3739    display: none !important;
3740  }
3741  
3742  .pd-price-current {
3743    font-size: 18pt !important;
3744    color: #0d6efd !important;
3745  }
3746  
3747  /* Show price range for variant products in print */
3748  .pd-price-current[data-price-range]::after {
3749    content: attr(data-price-range) !important;
3750    display: block !important;
3751    font-size: 11pt !important;
3752    color: #64748b !important;
3753    margin-top: 4px !important;
3754    font-weight: normal !important;
3755  }
3756  
3757  /* Hide 'Range:' prefix text */
3758  .pd-price-current[data-price-range]::after {
3759    content: attr(data-price-range) !important;
3760  }
3761  
3762  .pd-price-original {
3763    font-size: 12pt !important;
3764    color: #64748b !important;
3765  }
3766  
3767  .pd-discount-badge {
3768    font-size: 10pt !important;
3769    padding: 2px 6px !important;
3770  }
3771  
3772  /* QR Code section - right side beside price */
3773  .pd-qr-print {
3774    display: block !important;
3775    flex: 0 0 100px !important;
3776    margin: 0 !important;
3777    padding: 8px !important;
3778    background: #f8fafc !important;
3779    border-radius: 8px !important;
3780    text-align: center !important;
3781    page-break-inside: avoid !important;
3782    border: 1px solid #cbd5e1 !important;
3783  }
3784  
3785  .pd-qr-label {
3786    font-size: 6.5pt !important;
3787    color: #64748b !important;
3788    margin-bottom: 5px !important;
3789    line-height: 1.2 !important;
3790    font-weight: 600 !important;
3791  }
3792  
3793  .pd-qr-image {
3794    width: 80px !important;
3795    height: 80px !important;
3796    display: block !important;
3797    margin: 0 auto !important;
3798  }
3799  
3800  /* Meta info */
3801  .pd-meta-inline {
3802    display: flex !important;
3803    gap: 12px !important;
3804    margin-bottom: 0 !important;
3805    padding-bottom: 14px !important;
3806    font-size: 10pt !important;
3807    border: none !important;
3808    border-top: none !important;
3809    border-bottom: none !important;
3810    padding-top: 0 !important;
3811    box-shadow: none !important;
3812    text-decoration: none !important;
3813    background: transparent !important;
3814  }
3815  
3816  /* Hide stock badge in print */
3817  .stock-badge,
3818  .stock-info-btn {
3819    display: none !important;
3820  }
3821  
3822  .sku {
3823    color: #64748b !important;
3824  }
3825  
3826  /* Hide sold badge if empty/zero */
3827  .sold-inline {
3828    display: none !important;
3829  }
3830  
3831  /* Hide variant summary section in print */
3832  .pd-variant-combined,
3833  #pd-variant-combined,
3834  .pd-variant-summary,
3835  [data-role="sold-chip"] {
3836    display: none !important;
3837  }
3838  
3839  /* Hide compare section completely in print */
3840  .pd-compare-section,
3841  .pd-compare-btn,
3842  #pd-compare-btn,
3843  .pd-compare-info,
3844  .compare-note,
3845  .compare-text,
3846  .compare-remove {
3847    display: none !important;
3848    visibility: hidden !important;
3849    height: 0 !important;
3850    margin: 0 !important;
3851    padding: 0 !important;
3852    overflow: hidden !important;
3853  }
3854  
3855  /* Variants - show all options with selected highlighted */
3856  .pd-variants {
3857    margin: 0 !important;
3858    padding: 0 !important;
3859    page-break-inside: avoid !important;
3860    border: none !important;
3861    border-top: none !important;
3862    border-bottom: none !important;
3863    box-shadow: none !important;
3864    background: transparent !important;
3865  }
3866  
3867  /* Add 'Variant:' label before variant groups */
3868  .pd-variants::before {
3869    content: "Variant:" !important;
3870    display: block !important;
3871    font-size: 11pt !important;
3872    font-weight: 700 !important;
3873    color: #0f172a !important;
3874    margin-bottom: 10px !important;
3875    border: none !important;
3876    padding: 0 !important;
3877    border-top: none !important;
3878    border-bottom: none !important;
3879  }
3880  
3881  .v-group {
3882    display: flex !important;
3883    align-items: center !important;
3884    gap: 8px !important;
3885    margin-bottom: 8px !important;
3886    line-height: 1 !important;
3887    page-break-inside: avoid !important;
3888    border: none !important;
3889    padding: 0 !important;
3890  }
3891  
3892  .v-group:last-child {
3893    margin-bottom: 0 !important;
3894  }
3895  
3896  .v-label {
3897    font-weight: 700 !important;
3898    margin: 0 !important;
3899    font-size: 9pt !important;
3900    color: #475569 !important;
3901    min-width: 60px !important;
3902    flex-shrink: 0 !important;
3903    border: none !important;
3904    padding: 0 !important;
3905  }
3906  
3907  .v-options {
3908    display: flex !important;
3909    gap: 6px !important;
3910    flex-wrap: wrap !important;
3911    border: none !important;
3912    padding: 0 !important;
3913    margin: 0 !important;
3914  }
3915  
3916  .v-chip {
3917    border: 1px solid #cbd5e1 !important;
3918    padding: 3px 10px !important;
3919    font-size: 9pt !important;
3920    background: #f8fafc !important;
3921    border-radius: 6px !important;
3922    display: inline-flex !important;
3923    align-items: center !important;
3924    page-break-inside: avoid !important;
3925  }
3926  
3927  /* Highlight selected variant with blue background */
3928  .v-chip.selected {
3929    background: #eff6ff !important;
3930    border-color: #2563eb !important;
3931    font-weight: 600 !important;
3932  }
3933  
3934  /* Hide radio inputs */
3935  .v-chip input[type="radio"] {
3936    display: none !important;
3937  }
3938  
3939  /* Hide "Habis" badge in print */
3940  .v-chip .chip-badge {
3941    display: none !important;
3942  }
3943  
3944  /* Short description - HIDDEN in print */
3945  .pd-short-desc {
3946    display: none !important;
3947  }
3948  
3949  /* QR Code section - right side beside price */
3950  .pd-qr-print {
3951    display: block !important;
3952    flex: 0 0 100px !important;
3953    margin: 0 !important;
3954    padding: 8px !important;
3955    background: #f8fafc !important;
3956    border-radius: 8px !important;
3957    text-align: center !important;
3958    page-break-inside: avoid !important;
3959    border: 1px solid #cbd5e1 !important;
3960  }
3961  
3962  .pd-qr-label {
3963    font-size: 6.5pt !important;
3964    color: #64748b !important;
3965    margin-bottom: 5px !important;
3966    line-height: 1.2 !important;
3967    font-weight: 600 !important;
3968  }
3969  
3970  .pd-qr-image {
3971    width: 80px !important;
3972    height: 80px !important;
3973    display: block !important;
3974    margin: 0 auto !important;
3975  }
3976  
3977  /* Full description - below 2 column layout */
3978  .pd-full-description {
3979    border: none !important;
3980    padding: 0 !important;
3981    margin: 24px 0 0 0 !important;
3982    page-break-before: avoid;
3983    grid-column: 1 / -1 !important;
3984  }
3985  
3986  .pd-full-desc-title {
3987    font-size: 13pt !important;
3988    font-weight: 700 !important;
3989    margin: 0 0 12px 0 !important;
3990    color: #000 !important;
3991    border-top: none !important;
3992    border-bottom: none !important;
3993    padding-top: 16px !important;
3994  }
3995  
3996  .pd-full-desc-body {
3997    font-size: 10pt !important;
3998    line-height: 1.6 !important;
3999    color: #334155 !important;
4000  }
4001  
4002  .pd-full-desc-body img {
4003    max-height: 200px !important;
4004    width: auto !important;
4005    page-break-inside: avoid;
4006    display: none !important; /* Hide images in print to save space */
4007  }
4008  
4009  /* Also target #deskripsi img if exists */
4010  #deskripsi img {
4011    max-width: 350px !important;
4012    height: auto !important;
4013  }
4014  
4015  .pd-full-desc-body table {
4016    border-collapse: collapse !important;
4017    width: 100% !important;
4018    margin: 12px 0 !important;
4019    font-size: 9pt !important;
4020  }
4021  
4022  .pd-full-desc-body th,
4023  .pd-full-desc-body td {
4024    border: 1px solid #cbd5e1 !important;
4025    padding: 6px 8px !important;
4026  }
4027  
4028  /* Print footer */
4029  .product-single::after {
4030    content: "Plaza IT - plazait.co.id | Printed: " attr(data-print-date);
4031    display: block;
4032    margin-top: 30px;
4033    padding-top: 16px;
4034    border-top: 1px solid #e2e8f0;
4035    font-size: 9pt;
4036    color: #64748b;
4037    text-align: center;
4038  }
4039  
4040  /* Page breaks */
4041  .pd-media-card,
4042  .pd-price-block,
4043  .pd-full-desc-title {
4044    page-break-after: avoid;
4045  }
4046  
4047  .pd-full-description {
4048    page-break-before: auto;
4049  }
4050}
4051
4052/* === Breadcrumb (truncate last item) === */
4053.pd-breadcrumb{
4054  display:flex;
4055  flex-wrap:nowrap;
4056  align-items:center;
4057  gap:.5rem;
4058  font-size:.72rem;
4059  margin:.7rem 0 1.2rem .5rem;
4060  color:var(--pd-muted);
4061  overflow:hidden;
4062}
4063.pd-breadcrumb a{color:#ffffff;text-decoration:none}
4064.pd-breadcrumb a:hover{text-decoration:underline}
4065.pd-breadcrumb a,
4066.pd-breadcrumb span{flex:0 0 auto}
4067.pd-breadcrumb .current{
4068  flex:1 1 auto;
4069  min-width:0;
4070  white-space:nowrap;
4071  overflow:hidden;
4072  text-overflow:ellipsis;
4073  font-weight:600;
4074  color:var(--pd-dark);
4075  padding-right:2px;
4076}
4077
4078/* === Top layout === */
4079.pd-top-layout{
4080  display:grid;
4081  gap:var(--pd-gap);
4082  grid-template-columns:minmax(260px,340px) minmax(460px,1fr) 300px;
4083  align-items:start;
4084  margin-bottom:2.4rem;
4085}
4086
4087/* === Media column === */
4088.pd-media-card{
4089  background:#fff;
4090  border:1px solid var(--pd-border);
4091  border-radius:var(--pd-radius);
4092  padding:1rem 1rem 1.2rem;
4093}
4094.pd-main-image{margin:0 0 .8rem;border:1px solid var(--pd-soft-border);border-radius:10px;overflow:hidden;background:#fff;position:relative}
4095.pd-main-image img{width:100%;display:block;object-fit:contain;aspect-ratio:1/1}
4096
4097/* Gallery Thumbnails */
4098.pd-thumb-gallery{margin-top:.6rem;display:flex;flex-wrap:wrap;gap:.55rem}
4099.pd-thumb-btn{
4100  border:1px solid var(--pd-soft-border);
4101  background:#fff;
4102  cursor:pointer;
4103  width:64px;height:64px;padding:2px;
4104  display:flex;align-items:center;justify-content:center;
4105  overflow:hidden;border-radius:8px;
4106  transition:border-color .18s ease, box-shadow .18s ease, transform .18s ease;
4107  position:relative;
4108}
4109.pd-thumb-btn img{max-width:100%;max-height:100%;object-fit:contain;display:block}
4110.pd-thumb-btn:hover{border-color:#ffffff}
4111.pd-thumb-btn.active{border-color:#ffffff;box-shadow:0 0 0 2px rgba(13,110,253,.25)}
4112.pd-thumb-btn:focus-visible{outline:2px solid #ffffff;outline-offset:2px}
4113.pd-thumb-btn:active{transform:scale(.96)}
4114
4115/* === Info column === */
4116.pd-info-card{
4117  background:#fff;border:1px solid var(--pd-border);border-radius:var(--pd-radius);
4118  padding:1.4rem 1.6rem 2rem;
4119}
4120.pd-title{margin:0 0 1rem;font-size:1.2rem;line-height:1.25;font-weight:600;color:var(--pd-dark)}
4121.pd-price-block{margin:0 0 1rem}
4122.pd-price-row{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem;margin:0 0 .35rem}
4123.pd-price-current{font-size:2rem;font-weight:700;color:#ffffff}
4124.pd-price-original{font-size:.9rem;text-decoration:line-through;color:#94a3b8}
4125.pd-discount-badge{background:#821603;color:#fff;font-size:.68rem;padding:4px 8px 3px;font-weight:600;border-radius:6px;letter-spacing:.5px}
4126.pd-meta-inline{display:flex;flex-wrap:wrap;gap:.9rem;align-items:center;font-size:.63rem;letter-spacing:.6px;text-transform:uppercase;font-weight:600}
4127.stock-badge{padding:4px 10px;border-radius:24px;background:var(--pd-soft);color:var(--pd-muted);display:inline-block;line-height:1;vertical-align:middle}
4128.stock-badge.in{background:var(--pd-success-bg);color:var(--pd-success-text)}
4129.stock-badge.out{background:var(--pd-danger-bg);color:var(--pd-danger-text)}
4130.digital-badge{padding:4px 10px;border-radius:24px;background:linear-gradient(135deg,#FF5722 0%,#764ba2 100%);color:#fff;font-size:.63rem;font-weight:600;letter-spacing:.6px;text-transform:uppercase;display:inline-block;line-height:1;vertical-align:middle;border:none}
4131.sku{color:var(--pd-muted)}
4132.pd-short-desc{font-size:.85rem;line-height:1.5;margin:0 0 1.2rem;color:#334155;overflow-wrap:anywhere}
4133.pd-short-desc p{margin:0 0 .75rem}
4134.pd-short-desc ul,.pd-short-desc ol{margin:.5rem 0 .9rem 1.1rem;font-size:.85rem;line-height:1.45}
4135
4136/* Meta row for sold and rating badges */
4137.pd-meta-row {
4138  display: flex;
4139  align-items: center;
4140  flex-wrap: wrap;
4141  gap: 5px;
4142  margin-top: 8px;
4143  margin-bottom: 4px;
4144}
4145
4146/* Sales (Terjual) badge inline */
4147.sold-inline{
4148  display:inline-block;font-size:.65rem;font-weight:600;color:#475569;
4149  background:#f1f5f9;padding:3px 8px 4px;border-radius:6px;letter-spacing:.4px;margin-left:0;
4150}
4151
4152/* Price row divider */
4153.price-divider {
4154  display: inline-block;
4155  margin: 0 .4rem;
4156  color: #cbd5e1;
4157  font-weight: 400;
4158}
4159
4160/* Rating badge inline */
4161.rating-inline {
4162  display: inline-flex;
4163  align-items: center;
4164  gap: 4px;
4165  padding: 3px 8px 4px;
4166  background: #fff7ed;
4167  border-radius: 6px;
4168  font-size: .65rem;
4169  font-weight: 600;
4170  color: #ea580c;
4171  text-decoration: none;
4172  transition: all .2s ease;
4173  cursor: pointer;
4174  margin-left: .5rem;
4175  white-space: nowrap;
4176}
4177
4178.rating-inline:hover {
4179  background: #ffedd5;
4180  transform: translateY(-1px);
4181  box-shadow: 0 2px 4px rgba(234, 88, 12, 0.1);
4182}
4183
4184.rating-inline .star-icon {
4185  font-size: .75rem;
4186  color: #f59e0b;
4187  line-height: 1;
4188}
4189
4190.rating-inline .rating-value {
4191  color: #ea580c;
4192}
4193
4194.rating-inline .rating-badge-count {
4195  color: #9a3412;
4196  opacity: 0.85;
4197}
4198
4199@media (max-width: 640px) {
4200  .rating-inline {
4201    font-size: .65rem;
4202    padding: 3px 8px 4px;
4203  }
4204  .rating-inline .star-icon {
4205    font-size: .7rem;
4206  }
4207}
4208
4209/* Highlight animation for review section */
4210@keyframes highlightFlash {
4211  0%, 100% { background-color: transparent; }
4212  50% { background-color: rgba(251, 146, 60, 0.1); }
4213}
4214
4215.highlight-flash {
4216  animation: highlightFlash 1.5s ease;
4217}
4218
4219/* === Variant chips === */
4220.pd-variants{margin-top:14px}
4221.v-group{margin-bottom:12px}
4222.v-label{font-weight:600;margin-bottom:6px}
4223.v-options{display:flex;flex-wrap:wrap;gap:8px}
4224.v-chip{
4225  display:inline-flex;align-items:center;gap:8px;
4226  padding:6px 10px;border:1px solid #d6d9de;border-radius:999px;
4227  cursor:pointer;background:#fff;transition:.15s ease;user-select:none
4228}
4229.v-chip:hover{border-color:#9aa3af;box-shadow:0 1px 0 rgba(0,0,0,.04)}
4230.v-chip.selected{border-color:#2563eb;background:#eff6ff}
4231.v-chip.disabled{opacity:.45;cursor:not-allowed}
4232.v-chip.out{border-style:dashed}
4233.v-chip input{display:none}
4234.v-chip .dot{width:14px;
4234height:14px;border-radius:50%;background:var(--dot,#999);box-shadow:inset 0 0 0 1px rgba(0,0,0,.2)}
4235.v-chip .dot--neutral{background:#9aa3af}
4236.v-chip .txt{font-size:.95rem;white-space:nowrap}
4237.v-chip .chip-badge{margin-left:2px;font-size:.72rem;line-height:1;padding:3px 6px;border-radius:6px;background:#fee2e2;color:#991b1b;border:1px solid #fecaca}
4238.pd-variant-summary{margin-top:8px;color:#475569;font-size:.92rem}
4239.pd-variant-summary .vs-item{display:inline-flex;gap:4px;margin-right:10px}
4240
4241/* === Buy form (di sidebar kanan untuk desktop) === */
4242.pd-buy-form{display:flex;flex-wrap:wrap;align-items:flex-end;gap:1rem 1.4rem;margin:0}
4243.qty-label{display:block;font-size:.6rem;font-weight:600;letter-spacing:.7px;text-transform:uppercase;color:var(--pd-muted);margin-bottom:.35rem}
4244.pd-buy-form input[type=number]{width:90px;padding:.55rem .6rem;border:1px solid var(--pd-soft-border);border-radius:6px;font-size:.85rem}
4245.pd-actions{display:flex;gap:.8rem;flex-wrap:wrap}
4246
4247/* Tombol senada tema */
4248.btn-add-cart,.btn-buy,.btn-chat-icon{
4249  cursor:pointer;border:0;font-weight:700;border-radius:10px;font-size:.9rem;
4250  display:inline-flex;align-items:center;justify-content:center;gap:.4rem;
4251  min-height:44px;padding:0 1rem;transition:var(--pd-transition)
4252}
4253.btn-add-cart{background:var(--brand-500);color:#fff;box-shadow:0 2px 8px -2px rgba(13,110,253,.35)}
4254.btn-add-cart:hover{filter:brightness(.95)}
4255.btn-buy{background:#fff;color:var(--brand-500);border:2px solid var(--brand-500)}
4256.btn-buy:hover{background:rgba(13,110,253,.06)}
4257.btn-chat-icon{background:#fff;color:#fb923c;border:2px solid #fb923c;padding:0;width:44px;min-width:44px}
4258.btn-chat-icon:hover{background:rgba(251, 146, 60, 0.06);border-color:#f97316}
4259.btn-chat-icon svg{width:20px;height:20px;flex-shrink:0}
4260.btn-add-cart:disabled,.btn-buy:disabled,.btn-chat-icon:disabled{opacity:.5;cursor:not-allowed}
4261
4262/* Buy with Voucher Button - Cleaner, more compact design */
4263.btn-buy-voucher{
4264  cursor:pointer;border:0;font-weight:600;border-radius:10px;
4265  display:inline-flex;align-items:center;justify-content:space-between;gap:10px;
4266  min-height:44px;padding:0 14px 0 12px;transition:all 0.3s ease;
4267  background:linear-gradient(135deg, #7c3aed 0%, #9333ea 100%);
4268  color:#fff;
4269  box-shadow:0 3px 10px rgba(124,58,237,0.3);
4270  position:relative;
4271  overflow:hidden;
4272  flex:1 1 auto
4273}
4274.btn-buy-voucher:hover{
4275  transform:translateY(-2px);
4276  box-shadow:0 5px 16px rgba(124,58,237,0.4)
4277}
4278.btn-buy-voucher:active{
4279  transform:translateY(0);
4280  box-shadow:0 2px 6px rgba(124,58,237,0.3)
4281}
4282.btn-buy-voucher .bv-icon{
4283  font-size:20px;
4284  line-height:1;
4285  flex-shrink:0
4286}
4287.btn-buy-voucher .bv-text{
4288  display:flex;
4289  flex-direction:column;
4290  align-items:flex-start;
4291  gap:2px;
4292  flex:1;
4293  min-width:0;
4294  font-size:14px;
4295  line-height:1.2
4296}
4297.btn-buy-voucher .bv-text small{
4298  font-size:10px;
4299  opacity:0.85;
4300  font-weight:500;
4301  letter-spacing:0.3px;
4302  font-family:monospace
4303}
4304.btn-buy-voucher .bv-save{
4305  font-size:12px;
4306  font-weight:700;
4307  background:rgba(0,0,0,0.25);
4308  padding:4px 8px;
4309  border-radius:6px;
4310  white-space:nowrap;
4311  flex-shrink:0
4312}
4313
4314/* Extra meta (opsional) */
4315.pd-extra-meta{background:var(--pd-soft);border:1px solid var(--pd-border);border-radius:10px;padding:.85rem 1rem;display:grid;gap:.45rem;font-size:.7rem;line-height:1.3}
4316.pd-extra-meta span{font-weight:600;color:var(--pd-muted);margin-right:.3rem}
4317.pd-extra-meta a{color:#ffffff;text-decoration:none}
4318.pd-extra-meta a:hover{text-decoration:underline}
4319
4320/* === Sidebar === */
4321.pd-sidebar{display:flex;flex-direction:column;gap:1.2rem}
4322.pd-side-card{background:#fff;border:1px solid var(--pd-border);border-radius:var(--pd-radius);padding:1rem 1.1rem 1.2rem}
4323.pd-side-card h3{margin:0 0 .7rem;font-size:.8rem;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--pd-dark)}
4324.pd-side-card ul{list-style:none;margin:0;padding:0;display:grid;gap:.45rem;font-size:.7rem;color:#334155}
4325.pd-side-card li{position:relative;padding-left:13px;line-height:1.25}
4326.pd-side-card li:before{content:'';width:6px;height:6px;background:#ffffff;border-radius:50%;position:absolute;
4326left:0;top:.45rem}
4327.pay-logos{display:flex;gap:.4rem;flex-wrap:wrap;margin:.1rem 0 .4rem}
4328.pay-logos span{font-size:.55rem;background:var(--pd-soft);border:1px solid var(--pd-soft-border);padding:.35rem .55rem;border-radius:6px;font-weight:600;letter-spacing:.5px}
4329
4330/* Buy card hanya desktop (mobile pakai FAB) */
4331.pd-buy-card{display:block}
4332@media (max-width:880px){ .pd-buy-card{display:none} .pd-title-row {padding-right: 0 !important;}}
4333
4334/* === Full Description === */
4335.pd-full-description{background:#fff;border:1px solid var(--pd-border);border-radius:var(--pd-radius);padding:1.6rem 1.9rem .6rem;margin:0 0 .8rem}
4336.pd-full-desc-title{margin:0 0 1.1rem;font-size:1.15rem;font-weight:600;color:var(--pd-dark)}
4337.pd-full-desc-body{font-size:.9rem;line-height:1.6;color:#334155;overflow-wrap:anywhere}
4338.pd-full-desc-body p{margin:0 0 1rem}
4339.pd-full-desc-body ul,.pd-full-desc-body ol{margin:.5rem 0 1rem 1.2rem}
4340.pd-full-desc-body table{width:100%;border-collapse:collapse;margin:1rem 0 1.2rem}
4341.pd-full-desc-body table,.pd-full-desc-body th,.pd-full-desc-body td{border-bottom:1px dotted #cbd5e1;vertical-align: top}
4342.pd-full-desc-body th,.pd-full-desc-body td{padding:.45rem .55rem;text-align:left}
4343.pd-full-desc-body img{max-width:100%;height:auto;display:block;}
4344
4345/* === Related (container) === */
4346.pd-related-full{
4347  background:#fff;border:1px solid var(--pd-border);
4348  border-radius:var(--pd-radius);padding:1.4rem 1.6rem 1.9rem;
4349  margin:0 0 2rem;
4350}
4351.pd-related-title{margin:0 0 1.1rem;font-size:1.05rem;font-weight:600;color:var(--pd-dark)}
4352
4353/* Related Grid (dirapikan: gabung selector agar tidak duplikat) */
4354.pd-related-grid,
4355.pd-related-grid--full{
4356  display:grid !important;
4357  gap:16px !important;
4358  grid-template-columns:repeat(auto-fill,minmax(170px,1fr)) !important;
4359  align-items:stretch;
4360}
4361
4362/* Fallback jika grid tidak didukung */
4363@supports not (display:grid){
4364  .pd-related-grid,
4365  .pd-related-grid--full{display:flex !important;flex-wrap:wrap}
4366  .pd-related-grid .pd-related-card,
4367  .pd-related-grid--full .pd-related-card{flex:0 0 calc(20% - 16px);max-width:calc(20% - 16px)}
4368  @media (max-width:1100px){
4369    .pd-related-grid .pd-related-card,
4370    .pd-related-grid--full .pd-related-card{flex:0 0 calc(25% - 16px);max-width:calc(25% - 16px)}
4371  }
4372  @media (max-width:820px){
4373    .pd-related-grid .pd-related-card,
4374    .pd-related-grid--full .pd-related-card{flex:0 0 calc(33.333% - 16px);max-width:calc(33.333% - 16px)}
4375  }
4376  @media (max-width:580px){
4377    .pd-related-grid .pd-related-card,
4378    .pd-related-grid--full .pd-related-card{flex:0 0 calc(50% - 16px);max-width:calc(50% - 16px)}
4379  }
4380}
4381
4382/* Related Card */
4383.pd-related-card{
4384  display:flex;flex-direction:column;border:1px solid var(--pd-soft-border);
4385  background:#fff;border-radius:10px;text-decoration:none;color:inherit;
4386  overflow:hidden;transition:var(--pd-transition);position:relative;min-width:0;
4387}
4388.pd-related-card:hover{transform:translateY(-4px);box-shadow:var(--pd-shadow)}
4389.pd-related-card .thumb{aspect-ratio:1/1;background:#f1f5f9;overflow:hidden;position:relative}
4390.pd-related-card img{width:100%;height:100%;object-fit:cover;display:block}
4391.mini-disc{position:absolute;top:6px;left:6px;background:#dc2626;color:#fff;font-size:.55rem;padding:3px 6px 2px;border-radius:4px;font-weight:600}
4392.pd-related-card .info{padding:.55rem .65rem .75rem;display:flex;flex-direction:column;gap:.25rem}
4393.pd-related-card h3{font-size:.68rem;line-height:1.25;margin:0;height:2.5em;overflow:hidden;font-weight:600;color:#334155;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
4394.pd-related-card .price{font-size:.7rem;font-weight:600;color:#ffffff}
4395.pd-related-card .sold{display:block;font-size:.6rem;font-weight:600;color:#475569;margin-top:2px;letter-spacing:.3px;background:#f1f5f9;padding:2px 6px 3px;border-radius:5px; width: max-content;}
4396.pd-related-card .price .final {font-size: .82rem; font-weight: 700; color: #0d6efd;}
4397.pd-related-card .price .ori {font-size: .6rem; text-decoration: line-through; color: #94a3b8;font-weight: 500;}
4398
4399/* === Mobile slider (scroll-snap) — gambar utama tetap tampil === */
4400.pd-mobile-slider{display:none}
4401.pd-mobile-slider img{width:100%;display:block;object-fit:contain;aspect-ratio:1/1}
4402
4403/* Gallery counter badge */
4404.pd-gallery-counter{
4405  position:absolute;
4406  bottom:12px;
4407  left:12px;
4408  background:rgba(0,0,0,0.25);
4409  color:#9E9E9E;
4410  padding:4px 10px;
4411  border-radius:10px;
4412  font-size:12px;
4413  font-weight:600;
4414  z-index:100;
4415  backdrop-filter:blur(6px);
4416  -webkit-backdrop-filter:blur(6px);
4417  font-variant-numeric:tabular-nums;
4418  pointer-events:none;
4419  display:none;
4420  box-shadow:0 1px 4px rgba(0,0,0,.15);
4421  line-height:1;
4422  border:1px solid rgba(0,0,0,0.08);
4423}
4424
4425@media (max-width:640px){
4426  .pd-media-card{border: 0;padding: 0 1rem 0;}
4427  .pd-info-card {border: 0; padding: 0 1.3rem 0; }
4428  .pd-side-card {flex: 1 1 !important;}
4429  .pd-top-layout {gap: 0; margin-bottom: 0;}
4430  .pd-variants {margin-bottom: 1.5rem;}
4431  .pd-main-image{display:none}
4432  .pd-gallery-counter{display:block}
4433  .pd-mobile-slider-wrapper{
4434    position:relative;
4435    margin-bottom:.6rem;
4436  }
4437  .pd-mobile-slider{
4438    display:flex;gap:8px;overflow-x:auto;
4439    scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;
4440  }
4441  .pd-mslide{
4442    min-width:100%;flex:0 0 100%;scroll-snap-align:center;
4443    border:1px solid var(--pd-soft-border);border-radius:10px;overflow:hidden;background:#fff
4444  }
4445  .pd-thumb-gallery{display:none} /* hide thumbnails on mobile */
4446}
4447
4448/* === Mobile Floating Action Bar === */
4449.pd-mobile-fab{
4450  position:fixed;
4451  left:0;
4452  right:0;
4453  bottom:42px; /* Position above bottom-nav (60px min-height + 6px padding) */
4454  display:none;
4455  align-items:center;
4456  gap:.75rem;
4457  padding:12px 16px;
4458  background:rgba(0,0,0,0.95);
4459  backdrop-filter:blur(20px);
4460  -webkit-backdrop-filter:blur(20px);
4461  border-top:1px solid rgba(228,231,235,0.6);
4462  box-shadow:0 -4px 16px -4px rgba(0,0,0,0.12);
4463  z-index:999; /* Below bottom-nav (1000) */
4464  transition:bottom .3s cubic-bezier(0.4, 0.0, 0.2, 1);
4465}
4466
4467/* When bottom-nav is hidden, move FAB to bottom */
4468body:has(.bottom-nav.hidden) .pd-mobile-fab {
4469  bottom: 0;
4470}
4471.pd-mobile-fab .mf-actions{display:flex;gap:.5rem;width:100%}
4472.mf-btn{
4473  border-radius:10px;
4474  padding:.6rem 1rem;
4475  font-weight:600;
4476  font-size:14px;
4477  cursor:pointer;
4478  transition:all .2s;
4479  -webkit-tap-highlight-color:transparent;
4480  display:inline-flex;
4481  align-items:center;
4482  justify-content:center;
4483  gap:4px;
4484}
4485.mf-buy{
4486  background:#fb923c;
4487  color:#fff;
4488  border:0;
4489  box-shadow:0 2px 8px rgba(251, 146, 60, 0.3);
4490  flex:1;
4491  flex-direction:column;
4492  padding:.55rem 1rem;
4493  line-height:1;
4494  min-height:44px;
4495  font-family:inherit;
4496}
4497.mf-buy .mf-buy-text{
4498  font-size:11px;
4499  font-weight:600;
4500  opacity:0.95;
4501  font-family:inherit;
4502}
4503.mf-buy .mf-buy-price{
4504  font-size:13px;
4505  font-weight:800;
4506  letter-spacing:-0.3px;
4507  font-family:inherit;
4508  line-height:1;
4509}
4510.mf-buy:active{transform:scale(0.97)}
4511.mf-cart{
4512  background:#fff;
4513  color:#fb923c;
4514  border:2px solid #fb923c;
4515  width:44px;
4516  height:44px;
4517  min-height:44px;
4518  padding:0;
4519  flex-shrink:0;
4520  font-family:inherit;
4521}
4522.mf-cart:active{transform:scale(0.95)}
4523.mf-chat{background:#fff;color:#fb923c;border:2px solid #fb923c;width:44px;height:44px;min-height:44px;padding:0;flex-shrink:0;font-family:inherit}
4524.mf-chat:active{transform:scale(0.95)}
4525.mf-chat svg{width:20px;height:20px;flex-shrink:0}
4526.mf-share{
4527  background:#fff;
4528  color:#fb923c;
4529  border:2px solid #fb923c;
4530  width:44px;
4531  height:44px;
4532  padding:0;
4533  flex-shrink:0;
4534}
4535.mf-share:active{transform:scale(0.95)}
4536.mf-btn:disabled{opacity:.5;cursor:not-allowed}
4537@media (max-width:640px){
4538  .pd-mobile-fab{display:flex}
4539  .product-single{padding-bottom:0;} /* Space for FAB + bottom-nav */
4540}
4541
4542/* === Installment cards === */
4543.pd-installment-card{border:1px solid var(--pd-border);background:#fff;border-radius:var(--pd-radius);padding:1rem 1.1rem 1.25rem;display:flex;flex-direction:column;gap:.85rem}
4544.pd-installment-card h3{margin:0 0 .2rem;font-size:.78rem;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--pd-dark)}
4545
4546.inst-tabs{display:flex;gap:.4rem}
4547.inst-tab{flex:1 1 0;background:var(--pd-soft);border:1px solid var(--pd-soft-border);padding:.45rem .5rem;font-size:.63rem;font-weight:600;border-radius:6px;cursor:pointer;letter-spacing:.4px;transition:var(--pd-transition)}
4548.inst-tab.active,.inst-tab:hover{background:#ffffff;color:#fff;border-color:#ffffff}
4549.inst-tab:focus{outline:2px solid #ffffff;outline-offset:2px}
4550
4551.inst-tenors{display:flex;gap:.4rem;flex-wrap:wrap}
4552.tenor-btn{flex:1 0 auto;background:#fff;border:1px solid var(--pd-soft-border);padding:.42rem .55rem;font-size:.6rem;font-weight:600;border-radius:20px;cursor:pointer;transition:.18s;min-width:56px}
4553.tenor-btn.active,.tenor-btn:hover{background:#1d7df0;color:#fff;border-color:#1d7df0}
4554
4555.inst-result{display:flex;flex-direction:column;gap:.45rem;font-size:.63rem}
4556.inst-line{display:flex;justify-content:space-between;align-items:center;padding:.35rem .55rem;background:var(--pd-soft);border:1px solid var(--pd-soft-border);border-radius:6px}
4557.inst-line.highlight{background:#1d7df0;color:#fff;border-color:#1d7df0;font-weight:600}
4558.inst-line .lbl{opacity:.8}
4559.inst-line .val{font-weight:600;letter-spacing:.3px}
4560.inst-footnote{font-size:.53rem;line-height:1.25;color:#64748b;margin-top:.2rem}
4561@media (max-width: 860px){
4562  .pd-installment-card{order:3}
4563  .inst-tabs .inst-tab,.inst-tenors .tenor-btn{font-size:.65rem}
4564  
4565  /* Voucher button responsive - make it simpler */
4566  .btn-buy-voucher{
4567    padding:0 12px;
4568    gap:8px
4569  }
4570  .btn-buy-voucher .bv-icon{
4571    font-size:18px
4572  }
4573  .btn-buy-voucher .bv-text{
4574    font-size:13px
4575  }
4576  .btn-buy-voucher .bv-text small{
4577    font-size:9px
4578  }
4579  .btn-buy-voucher .bv-save{
4580    font-size:11px;
4581    padding:3px 6px
4582  }
4583}
4584
4585/* Installment List v2 (eye-catching) */
4586.pd-installment-card2{
4587  background:linear-gradient(135deg,#ffffff 0%,#f1f7ff 65%,#e8f2ff 100%);
4588  border:1px solid var(--pd-border);border-radius:var(--pd-radius);
4589  padding:1rem 1.05rem 1.2rem;display:flex;flex-direction:column;gap:.5rem;
4590  position:relative;overflow:hidden
4591}
4592.pd-installment-card2:before{
4593  content:'';position:absolute;width:140px;height:140px;
4594  background:radial-gradient(circle at center,rgba(29,125,240,.18),rgba(29,125,240,0));
4595  top:-40px;right:-40px;pointer-events:none
4596}
4597.pd-installment-card2 h3{margin:0;font-size:.8rem;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:#0f2f54}
4598.pd-installment-card2 .inst-head{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
4599.inst-head-badge{background:#1d7df0;color:#fff;font-size:.55rem;font-weight:600;padding:.25rem .5rem;border-radius:20px;letter-spacing:.4px;box-shadow:0 2px 6px -2px rgba(29,125,240,.6)}
4600
4601.inst-bank-tabs{display:flex;gap:.45rem}
4602.bank-tab{flex:1 1 0;background:#fff;border:1px solid var(--pd-soft-border);padding:.5rem .55rem;font-size:.63rem;font-weight:600;border-radius:7px;cursor:pointer;transition:.18s;letter-spacing:.3px}
4603.bank-tab.active,.bank-tab:hover{background:#1d7df0;color:#fff;border-color:#1d7df0;box-shadow:0 3px 10px -4px rgba(29,125,240,.6)}
4604.bank-tab:focus{outline:2px solid #1d7df0;outline-offset:2px}
4605
4606.inst-month-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.55rem}
4607.inst-month-item{display:flex;align-items:center;gap:.65rem;background:#fff;border:1px solid var(--pd-soft-border);border-radius:14px;padding:.55rem .7rem;position:relative;overflow:hidden;transition:.25s}
4608.inst-month-item:before{content:'';position:absolute;inset:0;opacity:0;background:linear-gradient(90deg,rgba(29,125,240,.08),rgba(29,125,240,0));transition:.25s}
4609.inst-month-item:hover{transform:translateY(-2px);box-shadow:0 6px 14px -6px rgba(0,40,120,.25)}
4610.inst-month-item:hover:before{opacity:1}
4611.tenor-badge{background:#1d7df0;color:#fff;font-weight:700;min-width:42px;text-align:center;padding:.38rem .4rem;font-size:.8rem;border-radius:10px;box-shadow:0 3px 10px -4px rgba(29,125,240,.6)}
4612.amount{flex:1;font-size:.8rem;font-weight:600;color:#0f2f54;letter-spacing:.3px}
4613.tag{font-size:.55rem;font-weight:600;padding:.28rem .55rem;border-radius:30px;letter-spacing:.4px;text-transform:uppercase;white-space:nowrap}
4614.tag-zero{background:#16a34a;color:#fff;box-shadow:0 2px 8px -3px rgba(16,180,90,.55)}
4615
4616.inst-note{font-size:.6rem;font-weight:600;color:#0f2f54;background:#e0f7ed;border:1px solid #b2ebd2;padding:.4rem .6rem;border-radius:8px;letter-spacing:.3px}
4617.inst-disclaimer{font-size:.52rem;line-height:1.25;color:#64748b;margin-top:-.2rem}
4618
4619@media (max-width:860px){
4620  .pd-installment-card2{order:3}
4621  .tenor-badge{min-width:38px;font-size:.75rem}
4622  .amount{font-size:.78rem}
4623}
4624
4625/* Alt Fintech Banner (compact) */
4626.inst-alt-banner--compact{
4627  background:linear-gradient(142deg,#1d7df0 0%,#3c98ff 55%,#67bdff 100%);
4628  color:#fff;border:0;padding:.95rem .95rem 1.05rem;border-radius:16px;
4629  box-shadow:0 6px 18px -6px rgba(0,70,160,.45);position:relative;overflow:hidden;min-height:auto;
4630}
4631.inst-alt-banner--compact:before,
4632.inst-alt-banner--compact:after{
4633  content:'';position:absolute;border-radius:50%;background:rgba(0,0,0,.15);filter:blur(2px);pointer-events:none;
4634}
4635.inst-alt-banner--compact:before{width:130px;height:130px;top:-50px;right:-35px}
4636.inst-alt-banner--compact:after{width:90px;height:90px;bottom:-40px;left:-30px}
4637.inst-alt-banner--compact .iab-top{margin:0 0 .55rem;display:flex;flex-direction:column;gap:.25rem}
4638.inst-alt-banner--compact .iab-title{font-size:.74rem;font-weight:700;letter-spacing:.7px;line-height:1.15}
4639.inst-alt-banner--compact .iab-sub{font-size:.55rem;font-weight:500;opacity:.95;letter-spacing:.4px}
4640
4641.iab-logos-compact{display:flex;gap:.55rem;align-items:center;margin:0 0 .6rem;flex-wrap:nowrap;justify-content:space-between}
4642.iab-logos-compact .logo-fin{width:30%}
4643.iab-logos-compact .logo{
4644  width:48px;height:48px;border-radius:14px;display:flex;align-items:center;justify-content:center;
4645  font-size:.62rem;font-weight:700;letter-spacing:.3px;position:relative;color:#fff;
4646  box-shadow:0 4px 10px -4px rgba(0,0,0,.35);background:rgba(0,0,0,.15);
4647  backdrop-filter:blur(4px);transition:.25s;text-shadow:0 1px 2px rgba(0,0,0,.4);user-select:none;
4648}
4649.iab-logos-compact .logo:before{
4650  content:'';position:absolute;inset:0;border-radius:inherit;
4651  background:linear-gradient(140deg,rgba(0,0,0,.35),rgba(0,0,0,0));mix-blend-mode:overlay;opacity:.55;pointer-events:none;
4652}
4653.iab-logos-compact .logo:hover{transform:translateY(-3px) scale(1.05);box-shadow:0 8px 18px -6px rgba(0,0,0,.45)}
4654
4655.iab-cta--compact{
4656  display:inline-flex;padding:.45rem .85rem;font-size:.58rem;font-weight:700;background:#fff;color:#0f3c6d;border-radius:30px;text-decoration:none;letter-spacing:.55px;box-shadow:0 4px 12px -5px rgba(0,0,0,.35);transition:.25s;
4657}
4658.iab-cta--compact:hover{transform:translateY(-2px);box-shadow:0 8px 18px -7px rgba(0,0,0,.45)}
4659.iab-footnote--compact{margin-top:.45rem;font-size:.46rem;opacity:.85;letter-spacing:.25px}
4660@media (max-width:860px){
4661  .inst-alt-banner--compact{order:4;margin-top:.4rem;flex:1 1}
4662  .iab-logos-compact .logo{width:46px;height:46px}
4663}
4664
4665/* === Scroll To Top (baru) === */
4666.pd-scroll-top{
4667  position:fixed;
4668  right:20px;
4669  bottom:90px;
4670  width:48px;
4671  height:48px;
4672  border-radius:50%;
4673  background:linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
4674  color:#0f2f54;
4675  border:1px solid rgba(226, 232, 240, 0.8);
4676  box-shadow:0 8px 20px -8px rgba(0,0,0,.3), 0 2px 6px rgba(0,0,0,.1);
4677  display:flex;
4678  align-items:center;
4679  justify-content:center;
4680  cursor:pointer;
4681  z-index:1105;
4682  opacity:0;
4683  transform:translateY(12px) scale(.92);
4684  transition:all .3s cubic-bezier(0.4, 0, 0.2, 1);
4685  backdrop-filter:blur(8px);
4686  -webkit-backdrop-filter:blur(8px);
4687}
4688.pd-scroll-top:hover{
4689  box-shadow:0 12px 28px -10px rgba(0,0,0,.4), 0 4px 12px rgba(0,0,0,.15);
4690  transform:translateY(0) scale(1);
4691  background:linear-gradient(135deg, #ffffff 0%, #fefefe 100%);
4692  border-color:rgba(226, 232, 240, 1);
4693}
4694.pd-scroll-top:active{
4695  transform:translateY(0) scale(.96);
4696}
4697.pd-scroll-top:focus-visible{outline:2px solid #ffffff;outline-offset:2px}
4698.pd-scroll-top.visible{opacity:1;transform:translateY(0) scale(1)}
4699@media (max-width:640px){
4700  /* Dynamic positioning via JavaScript */
4701  .pd-scroll-top{
4702    right:14px;
4703    bottom:var(--scroll-top-bottom, calc(68px + 18px + env(safe-area-inset-bottom)));
4704  }
4705}
4706
4707/* === Responsive layout tweaks === */
4708@media (max-width:1250px){
4709  .pd-top-layout{grid-template-columns:minmax(260px,330px) 1fr}
4710  .pd-sidebar{grid-column:1 / -1;flex-direction:row;flex-wrap:wrap;gap:1rem}
4711  /* Other cards 2-per-row; buy card always takes full row to avoid height-mismatch gaps */
4712  .pd-side-card{flex:1 1 calc(50% - .5rem)}
4713  .pd-buy-card{flex:0 0 100%}
4714}
4715@media (max-width:880px){
4716  .pd-top-layout{grid-template-columns:1fr}
4717  .pd-sidebar{flex-direction:column; padding: 0 1rem;}
4718  .pd-title{font-size:1.2rem}
4719  .pd-price-current{font-size:1.5rem}
4720}
4721@media (max-width:640px){
4722  /* Related: 2 kolom di mobile */
4723  .pd-related-full{padding:1.2rem;border:0;padding-top: .6rem;margin-bottom:.6rem;}
4724  .pd-related-grid,.pd-related-grid--full{grid-template-columns:repeat(2,1fr) !important;gap:12px !important}
4725}
4726@media (max-width:560px){
4727  .pd-actions{flex-direction:column}
4728  .btn-add-cart,.btn-buy,.btn-buy-voucher{width:100%}
4729  .pd-full-description{padding:1.2rem;border:0;padding-bottom:0;}
4730}
4731@media (max-width:380px){
4732  .pd-related-full{padding:.9rem .7rem 1.1rem}
4733  .pd-related-grid,.pd-related-grid--full{gap:8px !important}
4734  .pd-related-card .info{padding:.4rem .48rem .55rem}
4735}
4736
4737/* === Modern Countdown (Product Detail) === */
4738.pd-countdown-modern{
4739  --cd-bg1:#0ea5e9;          /* sky-500 */
4740  --cd-bg2:#821603;          /* indigo-500 */
4741  --cd-text:#000000;
4742  --cd-contrast:#ffffff;
4743  --cd-shadow: 0 8px 24px -10px rgba(0,0,0,.25);
4744  --cd-border: rgba(0,0,0,.25);
4745  --cd-box: rgba(0,0,0,.65);
4746  --cd-box-text:#0f172a;
4747  --cd-danger:#ef4444;
4748
4749  display:flex; gap:.75rem; align-items:center;
4750  background:linear-gradient(135deg,var(--cd-bg1),var(--cd-bg2));
4751  border:1px solid var(--cd-border);
4752  color:var(--cd-contrast);
4753  border-radius:14px;
4754  padding:.6rem .8rem;
4755  box-shadow:var(--cd-shadow);
4756  position:relative;
4757  overflow:hidden;
4758  margin-bottom: 1rem;
4759}
4760
4761.pd-countdown-modern .pd-cd-icon{
4762  width:28px;height:28px;flex:0 0 28px;
4763  display:grid;place-items:center;
4764  background:rgba(0,0,0,.22);
4765  border:1px solid var(--cd-border);
4766  border-radius:10px;
4767  box-shadow: inset 0 1px 0 rgba(0,0,0,.35);
4768}
4769.pd-countdown-modern .pd-cd-icon svg{width:18px;height:18px;fill:#fff;opacity:.95}
4770
4771.pd-countdown-modern .pd-cd-body{display:flex;flex-direction:column;gap:.35rem;min-width:0}
4772
4773.pd-countdown-modern .pd-cd-label{
4774  font-size:.72rem;font-weight:700;letter-spacing:.4px;opacity:.95;line-height:1;
4775  text-shadow:0 1px 0 rgba(0,0,0,.08);
4776}
4777
4778.pd-countdown-modern .pd-cd-time{
4779  display:flex; align-items:end; gap:.35rem; flex-wrap:wrap;
4780  line-height:1;
4781}
4782
4783.pd-countdown-modern .unit{
4784  display:flex; flex-direction:column; align-items:center; gap:.18rem;
4785  min-width:58px;
4786}
4787.pd-countdown-modern .num{
4788  display:inline-flex; align-items:center; justify-content:center;
4789  min-width:58px; height:40px; padding:0 .4rem;
4790  background:var(--cd-box);
4791  color:var(--cd-box-text);
4792  border:1px solid rgba(15,23,42,.08);
4793  border-radius:10px;
4794  font: 800 1.05rem ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
4795  letter-spacing:.5px;
4796  box-shadow: 0 4px 14px -6px rgba(0,0,0,.4), inset 0 1px 0 rgba(0,0,0,.55);
4797}
4798.pd-countdown-modern .unit-label{
4799  font-size:.62rem; font-weight:700; opacity:.9; text-shadow:0 1px 0 rgba(0,0,0,.15);
4800}
4801
4802.pd-countdown-modern .colon{
4803  font-weight:900; font-size:1.05rem; opacity:.95; transform: translateY(-2px);
4804  animation: cdBlink 1.2s infinite steps(2, jump-none);
4805}
4806@keyframes cdBlink { 0%{opacity:1} 50%{opacity:.25} 100%{opacity:1} }
4807
4808.pd-countdown-modern .pd-cd-progress{
4809  display:none;
4810  position:relative;
4811  height:6px; border-radius:999px; overflow:hidden;
4812  background:rgba(0,0,0,.25);
4813  border:1px solid rgba(0,0,0,.28);
4814}
4815.pd-countdown-modern .pd-cd-progress .bar{
4816  height:100%; width:0%; background:linear-gradient(90deg,#22c55e,#a3e635);
4817  box-shadow: 0 2px 8px rgba(0,0,0,.25) inset;
4818  transition:width .35s ease;
4819}
4820
4821/* State saat hampir habis */
4822.pd-countdown-modern.danger{
4823  --cd-bg1:#f97316; /* orange-500 */
4824  --cd-bg2:#ef4444; /* red-500   */
4825}
4826
4827/* Responsive */
4828@media (max-width:640px){
4829  .pd-countdown-modern{
4830    padding:.55rem .65rem; border-radius:12px; gap:.6rem;
4831  }
4832  .pd-countdown-modern .unit{min-width:52px}
4833  .pd-countdown-modern .num{min-width:52px;height:36px;font-size:.98rem}
4834}
4835
4836/* === Modern Countdown polish (shine + compact) === */
4837.pd-countdown-modern{
4838  position:relative;
4839}
4840.pd-countdown-modern::after{
4841  content:"";
4842  position:absolute; inset:-1px;
4843  background: linear-gradient(120deg, rgba(0,0,0,.12), rgba(0,0,0,0) 35%),
4844              radial-gradient(120% 120% at 0% 0%, rgba(0,0,0,.08), rgba(0,0,0,0) 60%);
4845  border-radius: inherit;
4846  pointer-events:none;
4847  mix-blend-mode: screen;
4848  animation: sheen 4.5s ease-in-out infinite;
4849}
4850@keyframes sheen {
4851  0%   { opacity:.35; transform: translateX(-10%) }
4852  50%  { opacity:.15; transform: translateX(4%) }
4853  100% { opacity:.35; transform: translateX(-10%) }
4854}
4855
4856/* Mode compact (aktifkan dengan data-mode="compact" di elemen) */
4857.pd-countdown-modern[data-mode="compact"]{
4858  gap:.55rem; padding:.5rem .6rem; border-radius:12px;
4859}
4860.pd-countdown-modern[data-mode="compact"] .unit{
4861  min-width:46px;
4862}
4863.pd-countdown-modern[data-mode="compact"] .num{
4864  min-width:46px; height:34px; font-size:.92rem;
4865}
4866.pd-countdown-modern[data-mode="compact"] .pd-cd-label{
4867  font-size:.66rem;
4868}
4869.pd-countdown-modern[data-mode="compact"] .pd-cd-icon{
4870  width:24px; height:24px; border-radius:8px;
4871}
4872.pd-countdown-modern[data-mode="compact"] .pd-cd-progress{
4873  height:5px;
4874}
4875
4876/* ============================================================
4877   Sidebar: shared refinements for medium + tablet (641px – 1250px)
4878   Buy card = form & paylater on the same row.
4879   Alt-fintech logos compact (no over-stretching).
4880   ============================================================ */
4881@media (min-width:641px) and (max-width:1250px) {
4882  /* Buy card: put qty/buy buttons and paylater promo inline */
4883  .pd-buy-card {
4884    display: flex !important;
4885    flex-direction: row;
4886    flex-wrap: wrap;
4887    align-items: center;
4888    gap: .6rem .75rem;
4889  }
4890  .pd-buy-card .pd-buy-form { flex: 1 1 0; min-width: 0; }
4891  .pd-buy-card .paylater-promo {
4892    flex: 0 0 auto;
4893    align-self: center;
4894    margin: 0;
4895    max-width: 44%;
4896  }
4897
4898  /* Alt-fintech banner: tighter logos */
4899  .iab-logos-compact {
4900    justify-content: center;
4901    gap: 1.25rem;
4902  }
4903  .iab-logos-compact .logo-fin {
4904    width: auto;
4905    max-height: 28px;
4906    object-fit: contain;
4907  }
4908}
4909
4910/* ============================================================
4911   Tablet layout (641px – 880px)
4912   Restores 2-column grid + horizontal sidebar for mid-size screens.
4913   Overrides the generic ≤880px mobile-collapse rules above.
4914   ============================================================ */
4915@media (min-width:641px) and (max-width:880px) {
4916  /* 2-column grid: image-card | info-card */
4917  .pd-top-layout {
4918    grid-template-columns: minmax(260px, 40%) 1fr;
4919    gap: 1rem;
4920  }
4921
4922  /* Sidebar: full-width horizontal flex row below the two main columns */
4923  .pd-sidebar {
4924    grid-column: 1 / -1;
4925    flex-direction: row;
4926    flex-wrap: wrap;
4927    gap: .75rem;
4928    padding: 0;
4929  }
4930  /* Banner cards fill 2-per-row */
4931  .pd-side-card { flex: 1 1 calc(50% - .375rem); }
4932  /* Buy card: full row (tallest card – avoids white-space gap if shared with shorter banners) */
4933  .pd-buy-card  { display: block !important; flex: 0 0 100%; }
4934
4935  /* Hide installment simulator + alt-fintech banner on tablet (too much vertical space) */
4936  .pd-installment-card2,
4937  .inst-alt-banner--compact { display: none !important; }
4938
4939  /* Restore sticky add-to-cart header (hidden at ≤880px by default) */
4940  .pd-sticky-header { display: flex !important; }
4941
4942  /* Typography – tighten slightly to fit the narrower info column */
4943  .pd-price-current { font-size: 1.65rem; }
4944  .pd-title         { font-size: 1.1rem;  }
4945  .pd-info-card     { padding: 1.1rem 1.2rem 1.5rem; }
4946}
4947</style>
4948  <style data-inlined="product-grid.css">
4949/* Product Grid (Firebird) */
4950
4951.product-archive {
4952  max-width: 1320px;
4953  margin-inline: auto;
4954  padding: 0 0 3rem;
4955  /*font-family: system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,sans-serif;*/
4956}
4957
4958.pa-toolbar {
4959  display: flex;
4960  flex-wrap: wrap;
4961  gap: 1rem;
4962  justify-content: space-between;
4963  align-items: flex-start;
4964}
4965
4966.pa-title {
4967  font-size: 1.4rem;
4968  margin: 0;
4969  font-weight: 600;
4970  line-height: 1.2;
4971  color: #0f172a;
4972}
4973
4974.pa-left {
4975  display: flex;
4976  flex-wrap: wrap;
4977  gap: .75rem;
4978  align-items: center;
4979}
4980
4981.pa-filter-badge {
4982  background: #f1f5f9;
4983  border: 1px solid #e2e8f0;
4984  padding: .45rem .6rem;
4985  border-radius: 8px;
4986  font-size: .7rem;
4987  display: flex;
4988  align-items: center;
4989  gap: .4rem;
4990  line-height: 1;
4991  color: #475569;
4992}
4993.pa-filter-badge strong { color: #0f172a; }
4994.pa-filter-badge .clear {
4995  display:inline-block;
4996  text-decoration:none;
4997  background:#e2e8f0;
4998  color:#475569;
4999  font-weight:600;
5000  padding:0 .35rem .05rem;
5001  border-radius:4px;
5002  transition:.2s;
5003  font-size:.75rem;
5004  line-height:1;
5005}
5006.pa-filter-badge .clear:hover { background:#cbd5e1; color:#1e293b; }
5007
5008.pa-right {
5009  display:flex;
5010  gap:1rem;
5011  flex-wrap:wrap;
5012  align-items:flex-start;
5013  margin-bottom: 0.5rem;
5014}
5015
5016.pa-search {
5017  display:flex;
5018  gap:.5rem;
5019  align-items:center;
5020  background:#fff;
5021  border:1px solid #e2e8f0;
5022  padding:.4rem .5rem;
5023  border-radius:8px;
5024}
5025.pa-search input[type=text] {
5026  border:0;
5027  outline:none;
5028  font-size:.8rem;
5029  padding:.35rem .4rem;
5030  min-width:200px;
5031}
5032.pa-search button {
5033  background:#0d6efd;
5034  border:0;
5035  color:#fff;
5036  font-size:.7rem;
5037  padding:.45rem .75rem;
5038  border-radius:6px;
5039  cursor:pointer;
5040  font-weight:600;
5041  letter-spacing:.3px;
5042  transition:.25s;
5043}
5044.pa-search button:hover { background:#0b5dc4; }
5045
5046.pa-sort {
5047  display:flex;
5048  gap:.4rem;
5049  align-items:center;
5050  font-size:.7rem;
5051  background:#fff;
5052  border:1px solid #e2e8f0;
5053  padding:.4rem .55rem;
5054  border-radius:8px;
5055}
5056.pa-sort select {
5057  font-size:.75rem;
5058  padding:.35rem .5rem;
5059  border:1px solid #cbd5e1;
5060  border-radius:6px;
5061  background:#fff;
5062  outline:none;
5063}
5064
5065.pa-empty {
5066  border:1px dashed #cbd5e1;
5067  padding:3rem 1rem;
5068  text-align:center;
5069  border-radius:12px;
5070  background:#fff;
5071}
5072.pa-empty p {
5073  margin:0 0 1rem;
5074  font-size:.9rem;
5075  color:#475569;
5076}
5077.btn-reset {
5078  display:inline-block;
5079  background:#0d6efd;
5080  color:#fff;
5081  font-size:.7rem;
5082  padding:.55rem 1rem;
5083  border-radius:8px;
5084  text-decoration:none;
5085  font-weight:600;
5086  letter-spacing:.4px;
5087  transition:.25s;
5088}
5089.btn-reset:hover { background:#0a58c7; }
5090
5091/* Grid */
5092.pa-grid {
5093  display:grid;
5094  gap:1rem;
5095  grid-template-columns:repeat(auto-fill, minmax(180px,1fr));
5096  align-items:stretch;
5097}
5098
5099.pa-card {
5100  background:#fff;
5101  border:1px solid #e2e8f0;
5102  border-radius:12px;
5103  display:flex;
5104  flex-direction:column;
5105  text-decoration:none;
5106  color:inherit;
5107  position:relative;
5108  overflow:hidden;
5109  transition:.28s cubic-bezier(.4,0,.2,1);
5110  min-width:0;
5111  padding: 0;
5112}
5113.pa-card:hover {
5114  transform:translateY(-4px);
5115  box-shadow:0 8px 22px -8px rgba(0,0,0,.15);
5116  border-color:#dce2ea;
5117}
5118
5119.pa-card .thumb {
5120  aspect-ratio:1/1;
5121  background:#f1f5f9;
5122  display:flex;
5123  align-items:center;
5124  justify-content:center;
5125  overflow:hidden;
5126  position:relative;
5127}
5128.pa-card img {
5129  width:100%;
5130  height:100%;
5131  object-fit:cover;
5132  display:block;
5133}
5134.badge-disc {
5135  position:absolute;
5136  top:8px;
5137  left:8px;
5138  background:#dc2626;
5139  color:#fff;
5140  font-size:.6rem;
5141  padding:4px 6px 3px;
5142  border-radius:6px;
5143  font-weight:700;
5144  letter-spacing:.4px;
5145}
5146
5147.pa-card .body {
5148  padding:.65rem .7rem .8rem;
5149  display:flex;
5150  flex-direction:column;
5151  gap:.45rem;
5152  min-height:90px;
5153}
5154
5155.pa-card .title {
5156  font-size:.75rem;
5157  line-height:1.25;
5158  margin:0;
5159  font-weight:600;
5160  color:#1e293b;
5161  height:2.4em;
5162  overflow:hidden;
5163}
5164
5165.pa-card .prices {
5166  display:flex;
5167  flex-wrap:wrap;
5168  align-items:baseline;
5169  gap:.4rem;
5170}
5171.pa-card .prices .final {
5172  font-size:.83rem;
5173  font-weight:700;
5174  color:#0d6efd;
5175}
5176.pa-card .prices .ori {
5177  font-size:.63rem;
5178  text-decoration:line-through;
5179  color:#94a3b8;
5180  font-weight:500;
5181}
5182
5183.pa-card .cat {
5184  margin-top:auto;
5185  font-size:.55rem;
5186  letter-spacing:.6px;
5187  text-transform:uppercase;
5188  background:#f1f5f9;
5189  border:1px solid #e2e8f0;
5190  padding:.28rem .45rem .25rem;
5191  border-radius:20px;
5192  font-weight:600;
5193  color:#475569;
5194  align-self:flex-start;
5195}
5196.pa-card .meta-line {
5197    margin-top:4px;
5198    font-size:.65rem;
5199    color:#475569;
5200}
5201.pa-card .meta-line .sold {
5202    display:inline-block;
5203    background:#f1f5f9;
5204    padding:2px 6px 3px;
5205    border-radius:4px;
5206    font-weight:600;
5207    letter-spacing:.3px;
5208    color:#334155;
5209}
5210
5211/* Pagination */
5212.pa-pagination {
5213  margin-top:2rem;
5214  display:flex;
5215  flex-wrap:wrap;
5216  gap:.4rem;
5217  justify-content:center;
5218  font-size:.7rem;
5219  align-items:center;
5220}
5221.pa-pagination a,
5222.pa-pagination span {
5223  min-width:34px;
5224  height:34px;
5225  display:flex;
5226  align-items:center;
5227  justify-content:center;
5228  border-radius:8px;
5229  text-decoration:none;
5230  font-weight:600;
5231  padding:0 .6rem;
5232  box-sizing:border-box;
5233  border:1px solid #e2e8f0;
5234  background:#fff;
5235  color:#334155;
5236  transition:.25s;
5237}
5238.pa-pagination a:hover {
5239  background:#f1f5f9;
5240  color:#0f172a;
5241}
5242.pa-pagination .current {
5243  background:#0d6efd;
5244  color:#fff;
5245  border-color:#0d6efd;
5246}
5247.pa-pagination .gap {
5248  border:none;
5249  background:transparent;
5250  font-weight:400;
5251  color:#94a3b8;
5252}
5253
5254/* =============== Mobile Filter Bar (Tokopedia-like) =============== */
5255.pa-mobile-filterbar{display:none}
5256@media (max-width: 640px){
5257  /* sembunyikan tombol lama */
5258  .pa-btn-filter{display:none}
5259
5260  .pa-mobile-filterbar{
5261    display:block;
5262    position:sticky;
5263    top:0; /* sesuaikan jika ada header fixed di atas */
5264    z-index:5;
5265    background:#fff;
5266    border-bottom:1px solid #e2e8f0;
5267    padding:.4rem .5rem;
5268    margin:-.4rem -1rem .6rem; /* full-bleed di container mobile */
5269  }
5270  .pa-mobile-filterbar .mf-scroll{
5271    display:flex;align-items:center;gap:.5rem;
5272    overflow-x:auto;
5273    -webkit-overflow-scrolling:touch;
5274    scrollbar-width:none;
5275  }
5276  .pa-mobile-filterbar .mf-scroll::-webkit-scrollbar{display:none}
5277
5278  .mf-chip{
5279    flex:0 0 auto;
5280    background:#fff;
5281    color:#334155;
5282    text-decoration:none;
5283    border:1px solid #e2e8f0;
5284    border-radius:999px;
5285    padding:.44rem .7rem;
5286    font-size:.78rem;
5287    font-weight:600;
5288    letter-spacing:.2px;
5289    white-space:nowrap;
5290  }
5291  .mf-chip:active{transform:scale(.98)}
5292  .mf-chip.active{
5293    background:#eaf2ff;
5294    border-color:#0d6efd;
5295    color:#0d6efd;
5296  }
5297
5298  .mf-filter-btn{
5299    position:relative;
5300    flex:0 0 auto;
5301    width:38px;height:38px;
5302    border-radius:50%;
5303    border:1.5px solid #0d6efd;
5304    background:#fff;
5305    display:inline-flex;align-items:center;justify-content:center;
5306    color:#0d6efd;
5307    cursor:pointer;
5308  }
5309  .mf-filter-btn:active{transform:scale(.96)}
5310  .mf-filter-btn svg{width:18px;height:18px;display:block}
5311  .mf-filter-btn .mf-badge{
5312    position:absolute;top:0;right:-4px;
5313    min-width:18px;height:18px;border-radius:9px;
5314    background:#ef4444;color:#fff;
5315    font-size:.65rem;font-weight:700;
5316    display:flex;align-items:center;justify-content:center;
5317    padding:0 4px;line-height:1;
5318    box-shadow:0 2px 6px -2px rgba(239,68,68,.6);
5319  }
5320}
5321
5322/* Responsive */
5323@media (max-width: 640px) {
5324  .product-archive { padding: 0 1rem 3rem; }
5325  .pa-grid { grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); }
5326  .pa-search input[type=text]{ min-width:140px; }
5327  .pa-toolbar { flex-direction:column; align-items:stretch; margin-bottom: 0; display: none;}
5328  .pa-right { display: none; }
5329  .pa-sort { width: 100%; justify-content:center; }
5330  .pa-mobile-filterbar { display: flex !important; }
5331}
5332
5333.pa-mobile-filterbar {
5334  display: none;
5335  align-items: center;
5336  gap: 8px;
5337  padding: 8px 0 8px 12px;
5338  background: #fff;
5339  width: 100vw;
5340}
5341.pa-mobile-filterbar .mf-scroll {
5342  display: flex;
5343  gap: 8px;
5344  overflow-x: auto;
5345  -webkit-overflow-scrolling: touch;
5346  scrollbar-width: none; /* Firefox */
5347  flex: 1 1 auto; /* takes remaining width */
5348}
5349.pa-mobile-filterbar .mf-scroll::-webkit-scrollbar { display: none; } /* WebKit */
5350.pa-mobile-filterbar .mf-chip {
5351  flex: 0 0 auto; /* prevent shrinking, keep width based on content */
5352  white-space: nowrap;
5353}
5354.pa-mobile-filterbar .mf-filter-btn {
5355  flex: 0 0 auto; /* do not shrink */
5356  position: relative; /* keep in place, not inside scroller */
5357  display: inline-flex;
5358  align-items: center;
5359  gap: 6px;
5360}
5361
5362/* Optional: ensure on small screens the button stays visible with min width */
5363@media (max-width: 768px) {
5364  .pa-mobile-filterbar .mf-filter-btn {
5365    padding: 6px 10px;
5366  }
5367}
5368
5369/* Product Specification Chips */
5370.pa-card .spec-chips {
5371  font-size: .6rem;
5372  color: #64748b;
5373  font-weight: 500;
5374  letter-spacing: .2px;
5375  margin-top: 2px;
5376  margin-bottom: 4px;
5377  line-height: 1.2;
5378  opacity: 0.9;
5379  max-width: 100%;
5380  overflow: hidden;
5381  text-overflow: ellipsis;
5382  white-space: nowrap;
5383}
5384
5385.pa-card .spec-chips .spec-divider {
5386  color: #94a3b8;
5387  font-weight: 400;
5388  margin: 0 1px;
5389}
5390</style>
5391  <style data-inlined="site-notice.css">
5392/**
5393 * Site Notice Banner - Reusable Notification System
5394 * Dismissible top bar for announcements, holidays, maintenance, etc.
5395 */
5396
5397.holiday-notice {
5398    position: fixed;
5399    top: 0;
5400    left: 0;
5401    right: 0;
5402    z-index: 9999;
5403    background: linear-gradient(135deg, #d32f2f 0%, #f57c00 100%);
5404    border-bottom: 2px solid rgba(255, 255, 255, 0.2);
5405    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
5406    animation: slideDown 0.4s cubic-bezier(0.4, 0, 0.2, 1);
5407    transform-origin: top;
5408}
5409
5410.holiday-notice.hidden {
5411    animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
5412}
5413
5414@keyframes slideDown {
5415    from {
5416        transform: translateY(-100%);
5417        opacity: 0;
5418    }
5419    to {
5420        transform: translateY(0);
5421        opacity: 1;
5422    }
5423}
5424
5425@keyframes slideUp {
5426    from {
5427        transform: translateY(0);
5428        opacity: 1;
5429    }
5430    to {
5431        transform: translateY(-100%);
5432        opacity: 0;
5433    }
5434}
5435
5436.holiday-notice-container {
5437    max-width: 1400px;
5438    margin: 0 auto;
5439    padding: 12px 20px;
5440    display: flex;
5441    align-items: center;
5442    justify-content: space-between;
5443    gap: 16px;
5444}
5445
5446.holiday-notice-content {
5447    display: flex;
5448    align-items: center;
5449    gap: 12px;
5450    flex: 1;
5451}
5452
5453.holiday-notice-icon {
5454    font-size: 20px;
5455    line-height: 1;
5456    flex-shrink: 0;
5457}
5458
5459.holiday-notice-text {
5460    color: #ffffff;
5461    font-size: 14px;
5462    font-weight: 500;
5463    line-height: 1.5;
5464    margin: 0;
5465    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
5466}
5467
5468.holiday-notice-emoji {
5469    display: inline-block;
5470    animation: bounce 2s ease-in-out infinite;
5471}
5472
5473@keyframes bounce {
5474    0%, 100% {
5475        transform: translateY(0);
5476    }
5477    50% {
5478        transform: translateY(-4px);
5479    }
5480}
5481
5482.holiday-notice-close {
5483    background: rgba(255, 255, 255, 0.2);
5484    border: 1px solid rgba(255, 255, 255, 0.3);
5485    border-radius: 6px;
5486    padding: 6px;
5487    cursor: pointer;
5488    color: #ffffff;
5489    transition: all 0.2s ease;
5490    flex-shrink: 0;
5491    display: flex;
5492    align-items: center;
5493    justify-content: center;
5494    width: 32px;
5495    height: 32px;
5496}
5497
5498.holiday-notice-close:hover {
5499    background: rgba(255, 255, 255, 0.3);
5500    transform: scale(1.05);
5501}
5502
5503.holiday-notice-close:active {
5504    transform: scale(0.95);
5505}
5506
5507.holiday-notice-close:focus {
5508    outline: 2px solid rgba(255, 255, 255, 0.5);
5509    outline-offset: 2px;
5510}
5511
5512/* Body padding adjustment when banner is visible */
5513body.has-holiday-notice {
5514    padding-top: 52px;
5515}
5516
5517/* Tablet Styles */
5518@media (max-width: 1024px) {
5519    .holiday-notice-container {
5520        padding: 10px 16px;
5521    }
5522    
5523    .holiday-notice-text {
5524        font-size: 13px;
5525    }
5526    
5527    .holiday-notice-icon {
5528        font-size: 18px;
5529    }
5530    
5531    body.has-holiday-notice {
5532        padding-top: 48px;
5533    }
5534}
5535
5536/* Mobile Styles */
5537@media (max-width: 640px) {
5538    .holiday-notice-container {
5539        padding: 10px 12px;
5540        gap: 8px;
5541    }
5542    
5543    .holiday-notice-content {
5544        gap: 8px;
5545    }
5546    
5547    .holiday-notice-text {
5548        font-size: 12px;
5549        line-height: 1.4;
5550    }
5551    
5552    .holiday-notice-icon {
5553        font-size: 16px;
5554    }
5555    
5556    .holiday-notice-close {
5557        width: 28px;
5558        height: 28px;
5559    }
5560    
5561    .holiday-notice-close svg {
5562        width: 16px;
5563        height: 16px;
5564    }
5565    
5566    body.has-holiday-notice {
5567        padding-top: 44px;
5568    }
5569}
5570
5571/* Ensure header doesn't get covered - removed z-index as it conflicts with dropdown stacking */
5572/* .header,
5573.navbar,
5574header {
5575    position: relative;
5576    z-index: 100;
5577} */
5578
5579</style>
5580        <style data-inlined="benchmark.css">
5581/**
5582 * Benchmark Section Styles
5583 * For product detail page benchmark display
5584 */
5585
5586.pd-benchmark-section {
5587    background: #fff;
5588    border-radius: 12px;
5589    padding: 24px;
5590    margin: 32px 0;
5591    box-shadow: 0 2px 8px rgba(0,0,0,0.06);
5592    border: 1px solid #e5e7eb;
5593}
5594
5595.benchmark-header {
5596    margin-bottom: 20px;
5597}
5598
5599.benchmark-title {
5600    font-size: 22px;
5601    font-weight: 700;
5602    color: #1a1a1a;
5603    margin: 0 0 12px 0;
5604    display: flex;
5605    align-items: center;
5606    gap: 8px;
5607}
5608
5609.benchmark-hint {
5610    font-size: 14px;
5611    color: #6b7280;
5612    margin: 0;
5613}
5614
5615/* Preview Badges */
5616.benchmark-preview-badges {
5617    display: flex;
5618    flex-wrap: wrap;
5619    gap: 10px;
5620    margin-top: 12px;
5621}
5622
5623.benchmark-badge {
5624    display: inline-flex;
5625    align-items: center;
5626    gap: 6px;
5627    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
5628    color: #fff;
5629    padding: 8px 16px;
5630    border-radius: 20px;
5631    font-size: 13px;
5632    font-weight: 500;
5633    box-shadow: 0 2px 6px rgba(102, 126, 234, 0.25);
5634    transition: all 0.2s ease;
5635}
5636
5637.benchmark-badge:hover {
5638    transform: translateY(-2px);
5639    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.35);
5640}
5641
5642.benchmark-badge strong {
5643    font-weight: 600;
5644    opacity: 0.9;
5645}
5646
5647/* Load Button */
5648.btn-load-benchmark {
5649    display: flex;
5650    align-items: center;
5651    justify-content: center;
5652    gap: 10px;
5653    width: 100%;
5654    max-width: 420px;
5655    padding: 14px 24px;
5656    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
5657    color: #fff;
5658    border: none;
5659    border-radius: 8px;
5660    font-size: 15px;
5661    font-weight: 600;
5662    cursor: pointer;
5663    transition: all 0.3s ease;
5664    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
5665    margin: 0 auto;
5666}
5667
5668.btn-load-benchmark:hover:not(:disabled) {
5669    transform: translateY(-2px);
5670    box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
5671}
5672
5673.btn-load-benchmark:disabled {
5674    opacity: 0.6;
5675    cursor: not-allowed;
5676}
5677
5678.btn-load-benchmark .btn-icon {
5679    font-size: 20px;
5680}
5681
5682.btn-load-benchmark.loading .btn-icon {
5683    animation: spin 1s linear infinite;
5684}
5685
5686@keyframes spin {
5687    from { transform: rotate(0deg); }
5688    to { transform: rotate(360deg); }
5689}
5690
5691/* Benchmark Content */
5692.benchmark-content {
5693    margin-top: 24px;
5694    padding-top: 24px;
5695    border-top: 2px solid #f3f4f6;
5696}
5697
5698.benchmark-loading {
5699    text-align: center;
5700    padding: 40px 20px;
5701    color: #6b7280;
5702}
5703
5704.benchmark-loading-spinner {
5705    width: 48px;
5706    height: 48px;
5707    margin: 0 auto 16px;
5708    border: 4px solid #f3f4f6;
5709    border-top-color: #667eea;
5710    border-radius: 50%;
5711    animation: spin 1s linear infinite;
5712}
5713
5714.benchmark-error {
5715    text-align: center;
5716    padding: 32px 20px;
5717    color: #ef4444;
5718    background: #fee2e2;
5719    border-radius: 8px;
5720}
5721
5722/* Benchmark Data Display */
5723.benchmark-data-grid {
5724    display: grid;
5725    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
5726    gap: 16px;
5727    margin-bottom: 24px;
5728}
5729
5730.benchmark-card {
5731    background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
5732    border: 1px solid #e5e7eb;
5733    border-radius: 10px;
5734    padding: 18px;
5735    text-align: center;
5736    transition: all 0.2s ease;
5737}
5738
5739.benchmark-card:hover {
5740    transform: translateY(-3px);
5741    box-shadow: 0 6px 16px rgba(102, 126, 234, 0.15);
5742}
5743
5744.benchmark-card-label {
5745    font-size: 13px;
5746    color: #6b7280;
5747    font-weight: 500;
5748    margin-bottom: 8px;
5749    text-transform: uppercase;
5750    letter-spacing: 0.5px;
5751}
5752
5753.benchmark-card-value {
5754    font-size: 24px;
5755    font-weight: 700;
5756    color: #1a1a1a;
5757    line-height: 1.2;
5758}
5759
5760.benchmark-card-value.large {
5761    font-size: 28px;
5762}
5763
5764.benchmark-card-subtitle {
5765    font-size: 12px;
5766    color: #9ca3af;
5767    margin-top: 4px;
5768}
5769
5770/* Spec Details */
5771.benchmark-specs {
5772    background: #f9fafb;
5773    border-radius: 10px;
5774    padding: 20px;
5775    margin-top: 20px;
5776}
5777
5778.benchmark-specs-title {
5779    font-size: 16px;
5780    font-weight: 600;
5781    color: #1a1a1a;
5782    margin: 0 0 16px 0;
5783}
5784
5785.benchmark-spec-row {
5786    display: flex;
5787    justify-content: space-between;
5788    padding: 10px 0;
5789    border-bottom: 1px solid #e5e7eb;
5790}
5791
5792.benchmark-spec-row:last-child {
5793    border-bottom: none;
5794}
5795
5796.benchmark-spec-label {
5797    font-size: 14px;
5798    color: #6b7280;
5799    font-weight: 500;
5800}
5801
5802.benchmark-spec-value {
5803    font-size: 14px;
5804    color: #1a1a1a;
5805    font-weight: 600;
5806    text-align: right;
5807}
5808
5809/* Score Badges */
5810.benchmark-score-badge {
5811    display: inline-block;
5812    padding: 6px 14px;
5813    border-radius: 20px;
5814    font-size: 13px;
5815    font-weight: 600;
5816    margin-top: 8px;
5817}
5818
5819.benchmark-score-badge.excellent {
5820    background: #10b981;
5821    color: #fff;
5822}
5823
5824.benchmark-score-badge.good {
5825    background: #3b82f6;
5826    color: #fff;
5827}
5828
5829.benchmark-score-badge.average {
5830    background: #f59e0b;
5831    color: #fff;
5832}
5833
5834.benchmark-score-badge.poor {
5835    background: #ef4444;
5836    color: #fff;
5837}
5838
5839/* Attribution */
5840.benchmark-attribution {
5841    text-align: center;
5842    margin-top: 24px;
5843    padding-top: 20px;
5844    border-top: 1px solid #e5e7eb;
5845    font-size: 13px;
5846    color: #9ca3af;
5847}
5848
5849.benchmark-attribution a {
5850    color: #667eea;
5851    text-decoration: none;
5852    font-weight: 500;
5853}
5854
5855.benchmark-attribution a:hover {
5856    text-decoration: underline;
5857}
5858
5859/* Responsive */
5860@media (max-width: 768px) {
5861    .pd-benchmark-section {
5862        padding: 16px;
5863        margin: 20px 0;
5864    }
5865    
5866    .benchmark-title {
5867        font-size: 18px;
5868    }
5869    
5870    .benchmark-preview-badges {
5871        flex-direction: column;
5872        gap: 8px;
5873    }
5874    
5875    .benchmark-badge {
5876        justify-content: center;
5877    }
5878    
5879    .btn-load-benchmark {
5880        font-size: 14px;
5881        padding: 12px 20px;
5882    }
5883    
5884    .benchmark-data-grid {
5885        grid-template-columns: 1fr 1fr;
5886        gap: 12px;
5887    }
5888    
5889    .benchmark-card {
5890        padding: 14px;
5891    }
5892    
5893    .benchmark-card-value {
5894        font-size: 20px;
5895    }
5896    
5897    .benchmark-spec-row {
5898        flex-direction: column;
5899        gap: 4px;
5900    }
5901    
5902    .benchmark-spec-value {
5903        text-align: left;
5904    }
5905}
5906
5907@media (max-width: 480px) {
5908    .benchmark-data-grid {
5909        grid-template-columns: 1fr;
5910    }
5911}
5912
5913</style>
5914    <style data-inlined="product-filters.css">
5915/* =========================================================
5916   Product Filters (Desktop + Mobile Drawer)
5917   Light Only + FIX:
5918   - Desktop: f-actions selalu tampil (static bottom)
5919   - Mobile: konten filter dapat discroll
5920   ========================================================= */
5921
5922/* ========== Layout wrapper (tidak mengubah grid produk) ========== */
5923.product-archive.with-filters .pa-layout {
5924  display:flex;
5925  gap:24px;
5926  align-items:flex-start;
5927}
5928.pa-main { flex:1; min-width:0; }
5929
5930/* =========================================================
5931   DESKTOP PANEL
5932   ========================================================= */
5933.pa-filters {
5934  width:300px;
5935  background:linear-gradient(145deg,#ffffff 0%,#f8fafc 55%,#f1f5f9 100%);
5936  border:1px solid #e2e8f0;
5937  padding:18px 18px 12px; /* bottom dikurangi karena f-actions punya padding sendiri */
5938  border-radius:18px;
5939  position:sticky;
5940  top:80px;
5941  height:calc(100vh - 100px); /* tinggi stabil untuk inner scroll */
5942  font-size:14px;
5943  box-shadow:0 1px 2px rgba(0,0,0,.04),0 4px 14px rgba(15,23,42,.08);
5944  backdrop-filter:blur(3px);
5945  -webkit-backdrop-filter:blur(3px);
5946  transition:box-shadow .3s, background .4s;
5947  display:flex;
5948  flex-direction:column;
5949  overflow:hidden; /* cegah double scroll, inner yang scroll */
5950}
5951.pa-filters::before {
5952  content:"";
5953  position:absolute;
5954  inset:0 auto 0 0;
5955  width:7px;
5956  border-radius:18px 0 0 18px;
5957  background:linear-gradient(180deg,#f59e0b 0%,#ffb53a 35%,#1d6ce0 100%);
5958}
5959.pa-filters.active { box-shadow:0 8px 32px -4px rgba(15,23,42,.35); }
5960
5961/* Scroll area (desktop/mode shared) */
5962.filters-scroll {
5963  flex:1;
5964  min-height:0;          /* wajib agar overflow terbaca dalam flex container */
5965  overflow:auto;
5966  padding-right:6px;
5967  margin-right:-6px;      /* kompensasi scrollbar */
5968  scrollbar-width:thin;
5969  overscroll-behavior:contain;
5970}
5971.filters-scroll::-webkit-scrollbar { width:8px; }
5972.filters-scroll::-webkit-scrollbar-thumb {
5973  background:#cbd5e1;
5974  border-radius:20px;
5975  border:2px solid #f1f5f9;
5976}
5977.filters-scroll::-webkit-scrollbar-thumb:hover { background:#94a3b8; }
5978
5979/* Heading */
5980.pa-filters h2 {
5981  margin:0 0 14px;
5982  font-size:20px;
5983  font-weight:700;
5984  letter-spacing:.4px;
5985  line-height:1.15;
5986  background:linear-gradient(90deg,#0f172a,#1d6ce0 60%,#0f172a);
5987  -webkit-background-clip:text;
5988  color:transparent;
5989  background-size:200% 100%;
5990  animation:titleSheen 8s linear infinite;
5991}
5992@keyframes titleSheen {
5993  0%{background-position:0 0}
5994  50%{background-position:100% 0}
5995  100%{background-position:0 0}
5996}
5997
5998/* Form groups */
5999.pa-filters .f-group { margin-bottom:18px; position:relative; }
6000.pa-filters label {
6001  display:block;
6002  font-weight:600;
6003  margin:0 0 6px;
6004  font-size:13px;
6005  color:#0f172a;
6006  letter-spacing:.3px;
6007}
6008
6009/* Inputs / selects */
6010.pa-filters input[type=text],
6011.pa-filters input[type=number],
6012.pa-filters select {
6013  width:100%;
6014  padding:7px 10px;
6015  border:1px solid #cbd5e1;
6016  border-radius:8px;
6017  font-size:13px;
6018  min-height:34px;
6019  background:#ffffff;
6020  box-sizing:border-box;
6021  color:#0f172a;
6022  transition:border-color .25s, box-shadow .25s, background .25s;
6023  -webkit-appearance:none;
6024  -moz-appearance:none;
6025  appearance:none;
6026}
6027.pa-filters select {
6028  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%230f172a' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
6029  background-repeat:no-repeat;
6030  background-position:right 10px center;
6031  background-size:12px;
6032  padding-right:32px;
6033}
6034.pa-filters input[type=text]:hover,
6035.pa-filters input[type=number]:hover,
6036.pa-filters select:hover { border-color:#94b5ec; }
6037.pa-filters input[type=text]:focus,
6038.pa-filters input[type=number]:focus,
6039.pa-filters select:focus {
6040  outline:none;
6041  border-color:#1d6ce0;
6042  box-shadow:0 0 0 3px rgba(29,108,224,.25);
6043  background:#f8fbff;
6044}
6045.pa-filters .range { display:flex; gap:10px; }
6046.pa-filters .range input { flex:1; }
6047.pa-filters .hint { font-size:11px; color:#64748b; margin-top:6px; }
6048
6049/* Spec blocks */
6050.f-specs { margin-top:4px; }
6051.spec-attr {
6052  margin:0 0 14px;
6053  padding:12px 12px 10px;
6054  border:1px solid #e2e8f0;
6055  border-radius:14px;
6056  background:linear-gradient(130deg,#ffffff 0%,#f7faff 85%);
6057  position:relative;
6058  transition:border-color .3s, box-shadow .3s, background .4s;
6059}
6060.spec-attr.collapsed { padding-bottom:8px; }
6061.spec-attr.has-selected {
6062  border-color:#1d6ce0;
6063  box-shadow:0 0 0 2px rgba(29,108,224,.15),0 2px 8px -2px rgba(29,108,224,.35);
6064  background:linear-gradient(135deg,#ffffff 0%,#e9f2ff 100%);
6065}
6066.spec-attr-title {
6067  font-weight:600;
6068  margin:0 0 8px;
6069  font-size:13px;
6070  display:flex;
6071  justify-content:space-between;
6072  align-items:center;
6073  cursor:pointer;
6074  color:#0f172a;
6075  user-select:none;
6076}
6077.spec-attr-title .chevron {
6078  font-size:10px;
6079  transition:transform .25s, opacity .25s;
6080  opacity:.65;
6081}
6082.spec-attr.collapsed .spec-attr-title .chevron {
6083  transform:rotate(-90deg);
6084  opacity:.5;
6085}
6086
6087/* Spec options */
6088.spec-attr-options { display:flex; flex-wrap:wrap; gap:6px; }
6089.spec-attr-options label {
6090  display:inline-flex;
6091  align-items:center;
6092  gap:4px;
6093  font-weight:500;
6094  border:1px solid #d4dde7;
6095  padding:6px 9px;
6096  border-radius:30px;
6097  cursor:pointer;
6098  background:#f3f6fa;
6099  font-size:12px;
6100  line-height:1.1;
6101  transition:background .25s, border-color .25s, color .25s, box-shadow .25s;
6102  position:relative;
6103}
6104.spec-attr-options label:hover { background:#e8f1ff; border-color:#b1cff3; }
6105.spec-attr-options input {
6106  margin:0;
6107  position:absolute;
6108  inset:0;
6109  opacity:0;
6110  cursor:pointer;
6111}
6112.spec-attr-options label:has(input:checked) {
6113  background:linear-gradient(135deg,#1d6ce0 0%,#155ab8 100%);
6114  border-color:#1d6ce0;
6115  color:#ffffff;
6116  box-shadow:0 2px 6px -1px rgba(29,108,224,.55);
6117}
6118.spec-attr-options label:has(input:checked)::after {
6119  content:"";
6120  position:absolute;
6121  right:6px;
6122  top:6px;
6123  width:6px;
6124  height:6px;
6125  background:#fff;
6126  border-radius:50%;
6127  box-shadow:0 0 0 4px rgba(0,0,0,.25),0 0 0 7px rgba(29,108,224,.5);
6128  animation:pulseDot 1.9s ease-out infinite;
6129}
6130@keyframes pulseDot {
6131  0%{transform:scale(.8);opacity:.9}
6132  60%{transform:scale(1.4);opacity:.15}
6133  100%{transform:scale(.8);opacity:.9}
6134}
6135
6136/* Badges */
6137.pa-filter-badges { margin:0 0 18px; display:flex; flex-wrap:wrap; gap:6px; }
6138.pa-filter-badge {
6139  display:inline-flex;
6140  gap:6px;
6141  align-items:center;
6142  background:linear-gradient(135deg,#eef4ff 0%,#e0edff 100%);
6143  border:1px solid #b4cff3;
6144  padding:5px 10px 5px 12px;
6145  border-radius:22px;
6146  margin:2px 4px 0 0;
6147  font-size:11px;
6148  font-weight:500;
6149  line-height:1;
6150  color:#0f172a;
6151  position:relative;
6152  overflow:hidden;
6153}
6154.pa-filter-badge::before {
6155  content:"";
6156  position:absolute;
6157  inset:0;
6158  background:linear-gradient(90deg,rgba(0,0,0,0),rgba(0,0,0,.35),rgba(0,0,0,0));
6159  transform:translateX(-100%);
6160  animation:badgeSheen 6s linear infinite;
6161}
6162@keyframes badgeSheen {
6163  0%{transform:translateX(-100%)}
6164  55%{transform:translateX(120%)}
6165  100%{transform:translateX(120%)}
6166}
6167.pa-filter-badge a.clear {
6168  text-decoration:none;
6169  color:#c02828;
6170  font-weight:700;
6171  padding:0 3px;
6172  display:inline-flex;
6173  align-items:center;
6174  justify-content:center;
6175  border-radius:50%;
6176  line-height:1;
6177  transition:background .25s, color .25s;
6178}
6179.pa-filter-badge a.clear:hover { background:#ffeded; color:#a40000; }
6180.pa-btn-filter { width: 100%;}
6181
6182/* =========================================================
6183   ACTIONS (DESKTOP) - STATIC BOTTOM
6184   ========================================================= */
6185.f-actions {
6186  margin-top:10px;
6187  display:flex;
6188  gap:12px;
6189  flex-wrap:wrap;
6190  padding:16px 0 4px;
6191  border-top:1px solid #e2e8f0;
6192  background:linear-gradient(180deg,rgba(0,0,0,0) 0%,#f1f5f9 70%);
6193  flex-shrink:0;
6194}
6195.f-actions button {
6196  background:linear-gradient(135deg,#1d6ce0 0%,#155ab8 100%);
6197  color:#fff;
6198  border:none;
6199  padding:10px 20px;
6200  border-radius:12px;
6201  cursor:pointer;
6202  font-size:14px;
6203  font-weight:600;
6204  letter-spacing:.3px;
6205  display:inline-flex;
6206  align-items:center;
6207  gap:6px;
6208  box-shadow:0 4px 14px -2px rgba(29,108,224,.45);
6209  transition:transform .25s, box-shadow .3s;
6210}
6211.f-actions button:hover { transform:translateY(-3px); box-shadow:0 8px 26px -4px rgba(29,108,224,.6); }
6212.f-actions button:active { transform:translateY(-1px) scale(.98); box-shadow:0 4px 14px -4px rgba(29,108,224,.55); }
6213.f-actions .reset {
6214  font-size:12px;
6215  text-decoration:none;
6216  color:#d92d2d;
6217  align-self:center;
6218  font-weight:600;
6219  padding:4px 6px;
6220  border-radius:8px;
6221  transition:background .25s, color .25s;
6222}
6223.f-actions .reset:hover { background:#ffe6e6; color:#b80000; text-decoration:underline; }
6224.pa-filters .close-drawer { display: none; }
6225
6226/* Form wrapper: flex container agar inner .filters-scroll bisa scroll di desktop */
6227.filter-form {
6228  flex: 1;
6229  min-height: 0;
6230  display: flex;
6231  flex-direction: column;
6232  overflow: hidden;
6233}
6234
6235/* =========================================================
6236   FILTER TOGGLE (MOBILE TRIGGER)
6237   ========================================================= */
6238.filter-toggle {
6239  display:none;
6240  margin:0 0 14px;
6241  background:linear-gradient(135deg,#1d6ce0 0%,#155ab8 100%);
6242  color:#fff;
6243  border:none;
6244  padding:11px 18px;
6245  font-size:14px;
6246  font-weight:600;
6247  border-radius:14px;
6248  cursor:pointer;
6249  letter-spacing:.4px;
6250  box-shadow:0 4px 14px -2px rgba(29,108,224,.55);
6251  position:relative;
6252  z-index:100;
6253  transition:box-shadow .3s, transform .25s;
6254}
6255.filter-toggle:hover { box-shadow:0 8px 26px -4px rgba(29,108,224,.6); transform:translateY(-2px); }
6256.filter-toggle:active { transform:translateY(0); box-shadow:0 4px 14px -4px rgba(29,108,224,.55); }
6257.filter-toggle .count-badge {
6258  display:inline-block;
6259  background:linear-gradient(135deg,#f59e0b 0%,#ffb43d 55%,#f59e0b 100%);
6260  color:#1d2b44;
6261  font-size:11px;
6262  font-weight:700;
6263  padding:3px 7px 4px;
6264  border-radius:30px;
6265  margin-left:8px;
6266  box-shadow:0 0 0 2px #ffffff,0 0 0 4px #1d6ce0;
6267}
6268
6269/* Overlay (mobile) */
6270.pa-filters-overlay { display:none; }
6271
6272/* =========================================================
6273   MOBILE DRAWER
6274   ========================================================= */
6275@media (max-width:768px){
6276  .product-archive.with-filters .pa-layout { flex-direction:column; gap:12px; }
6277  .filter-toggle { display:inline-flex; align-items:center; gap:6px; }
6278
6279  .pa-filters-overlay {
6280    position:fixed;
6281    inset:0;
6282    background:rgba(0,20,45,.55);
6283    z-index:998;
6284    opacity:0;
6285    transition:opacity .35s;
6286    display:block;
6287    pointer-events:none;
6288    backdrop-filter:blur(2px);
6289  }
6290  .pa-filters-overlay.active { opacity:1; pointer-events:auto; }
6291
6292  .pa-filters {
6293    position:fixed;
6294    top:auto;
6295    left:0; right:0; bottom:0;
6296    max-height:92vh;
6297    height:auto;
6298    width:100%;
6299    border-radius:30px 30px 0 0;
6300    border:1px solid #dbe4ef;
6301    padding:26px 22px 0;
6302    z-index:10000; /* di atas bottom-nav (9998) */
6303    transform:translateY(102%);
6304    transition:transform .55s cubic-bezier(.68,-0.35,.25,1);
6305    box-shadow:0 -4px 40px -6px rgba(15,23,42,.45);
6306    overflow:hidden; /* outer hidden, inner scroll */
6307  }
6308  .pa-filters.active { transform:translateY(0); }
6309
6310  /* Drag handle indicator */
6311  .pa-filters::after {
6312    content:"";
6313    display:block;
6314    position:absolute;
6315    top:10px;
6316    left:50%;
6317    transform:translateX(-50%);
6318    width:36px;
6319    height:4px;
6320    background:#cbd5e1;
6321    border-radius:4px;
6322  }
6323  body.filter-open { overflow:hidden; touch-action:none; }
6324
6325  /* Restore mobile-friendly input sizes (prevent iOS zoom on focus) */
6326  .pa-filters input[type=text],
6327  .pa-filters input[type=number],
6328  .pa-filters select {
6329    font-size:16px;
6330    min-height:44px;
6331    padding:10px 12px;
6332    border-radius:10px;
6333  }
6334  .pa-filters select { padding-right:32px; }
6335
6336  .filters-scroll {
6337    flex:1;
6338    overflow:auto;
6339    padding:0 0 120px; /* ruang di atas bar actions */
6340    margin:0;
6341    -webkit-overflow-scrolling:touch;
6342  }
6343
6344  .pa-filters h2 { font-size:19px; margin-bottom:10px; }
6345
6346  .pa-filters .close-drawer {
6347    display: block;
6348    position:absolute;
6349    right:14px; top:12px;
6350    background:linear-gradient(135deg,#dc2626 0%,#b91c1c 100%);
6351    color:#fff;
6352    padding:7px 14px;
6353    border:none;
6354    border-radius:10px;
6355    font-size:12px;
6356    font-weight:600;
6357    cursor:pointer;
6358    box-shadow:0 3px 10px -2px rgba(220,38,38,.55);
6359    transition:transform .25s, box-shadow .3s;
6360    z-index: 99;
6361  }
6362  .pa-filters .close-drawer:hover { transform:translateY(-2px); box-shadow:0 6px 18px -4px rgba(220,38,38,.6); }
6363
6364  .spec-attr { border-radius:18px; }
6365  .spec-attr-options label { font-size:11px; padding:5px 9px; }
6366
6367  /* Actions fixed di viewport bawah */
6368  .f-actions {
6369    position:fixed;
6370    left:0; right:0; bottom:0;
6371    margin:0;
6372    border-top:1px solid #d6dee8;
6373    padding:14px 16px calc(16px + env(safe-area-inset-bottom));
6374    background:#ffffffea;
6375    backdrop-filter:blur(6px);
6376    box-shadow:0 -4px 22px -6px rgba(15,23,42,.25);
6377    justify-content:space-between;
6378    z-index:10001; /* di atas pa-filters (10000) dan bottom-nav (9998) */
6379  }
6380
6381  /* Sembunyikan bottom-nav saat filter drawer terbuka */
6382  body.filter-open .bottom-nav {
6383    display: none !important;
6384  }
6385  .f-actions button {
6386    flex:1 1 auto;
6387    justify-content:center;
6388    padding:14px 14px;
6389    font-size:15px;
6390    border-radius:14px;
6391  }
6392  .f-actions .reset {
6393    order:-1;
6394    font-size:12px;
6395    padding:2px 8px;
6396  }
6397}
6398
6399/* Scrollbar (outer panel desktop only kept for compatibility) */
6400.pa-filters::-webkit-scrollbar { width:0; } /* disembunyikan karena inner yang scroll */
6401
6402/* Utilities */
6403.hidden { display:none !important; }
6404.gradient-text {
6405  background:linear-gradient(90deg,#1d6ce0,#155ab8,#1d6ce0);
6406  -webkit-background-clip:text;
6407  color:transparent;
6408  animation:titleSheen 10s linear infinite;
6409}
6410.glow-focus:focus {
6411  outline:none !important;
6412  box-shadow:0 0 0 3px rgba(29,108,224,.4) !important;
6413  border-color:#1d6ce0 !important;
6414}
6415
6416/* Simple fade (opsional) */
6417@keyframes fadeIn {
6418  from { opacity:0; transform:translateY(8px); }
6419  to { opacity:1; transform:translateY(0); }
6420}
6421</style>
6422  <style data-inlined="mega-menu-tiles.css">
6423/* Tile Mega Menu — Modern E-commerce Enhanced (2025) */
6424
6425/* Design Tokens */
6426:root {
6427  --mm2-bg: #ffffff;
6428  --mm2-surface: #ffffff;
6429  --mm2-surface-elevated: rgba(0,0,0,0.98);
6430  --mm2-muted: #f8fafc;
6431  --mm2-border: #e2e8f0;
6432  --mm2-border-strong: #cbd5e1;
6433  --mm2-accent: #821603;
6434  --mm2-accent-hover: #4f46e5;
6435  --mm2-accent-10: rgba(99,102,241,.10);
6436  --mm2-accent-20: rgba(99,102,241,.20);
6437  --mm2-fg: #0f172a;
6438  --mm2-fg-muted: #64748b;
6439  --mm2-fg-light: #94a3b8;
6440  
6441  /* Enhanced Shadows - Multi-layer elevation */
6442  --mm2-shadow-sm: 0 2px 8px rgba(15,23,42,.06), 0 1px 2px rgba(15,23,42,.04);
6443  --mm2-shadow-md: 0 8px 24px rgba(15,23,42,.10), 0 2px 8px rgba(15,23,42,.06);
6444  --mm2-shadow-lg: 0 20px 60px rgba(15,23,42,.14), 0 8px 24px rgba(15,23,42,.08);
6445  --mm2-shadow-xl: 0 32px 80px rgba(15,23,42,.18), 0 12px 32px rgba(15,23,42,.10);
6446  --mm2-shadow-hover: 0 16px 40px rgba(15,23,42,.16), 0 6px 16px rgba(15,23,42,.08);
6447  
6448  /* Border Radius */
6449  --mm2-radius-xl: 20px;
6450  --mm2-radius-lg: 16px;
6451  --mm2-radius-md: 12px;
6452  --mm2-radius-sm: 10px;
6453  
6454  --mm2-z: 60;
6455  
6456  /* Animation Timing */
6457  --mm2-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
6458  --mm2-transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1);
6459  --mm2-transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1);
6460}
6461
6462.navbar { position: relative; z-index: var(--mm2-z); }
6463
6464/* Trigger Button - Enhanced */
6465.mm2-trigger {
6466  display: inline-flex; align-items: center; gap: 8px;
6467  padding: 10px 14px; border-radius: var(--mm2-radius-md);
6468  border: 1.5px solid var(--mm2-border);
6469  background: var(--mm2-surface); color: var(--mm2-fg);
6470  cursor: pointer; user-select: none;
6471  transition: all var(--mm2-transition-fast);
6472  text-decoration: none;
6473  font-weight: 600;
6474  box-shadow: var(--mm2-shadow-sm);
6475}
6476.mm2-trigger:hover { 
6477  border-color: var(--mm2-accent); 
6478  background: var(--mm2-muted);
6479  box-shadow: var(--mm2-shadow-md);
6480  transform: translateY(-1px);
6481}
6482.mm2-trigger:active { transform: translateY(0); }
6483.mm2-trigger svg {max-width: unset !important; transition: transform var(--mm2-transition-fast); }
6484.mm2-trigger:hover svg { transform: scale(1.05); }
6485
6486/* Backdrop - Enhanced Glassmorphism */
6487.mm2-backdrop {
6488  position: fixed; 
6489  inset: 0;
6490  top: 0;
6491  left: 0;
6492  right: 0;
6493  bottom: 0;
6494  background: rgba(15,23,42,.40);
6495  backdrop-filter: blur(8px) saturate(1.2);
6496  -webkit-backdrop-filter: blur(8px) saturate(1.2);
6497  opacity: 0; 
6498  pointer-events: none;
6499  z-index: 499;
6500  transition: opacity var(--mm2-transition-base);
6501}
6502.mm2-open .mm2-backdrop { opacity: 1; pointer-events: auto; }
6503
6504/* Panel - Glassmorphism Enhanced */
6505.mm2-panel {
6506  position: fixed;
6507  left: 50%;
6508  top: 120px;
6509  transform: translateX(-50%) translateY(-16px);
6510  width: min(1200px, 95vw);
6511  background: var(--mm2-surface-elevated);
6512  backdrop-filter: blur(20px) saturate(1.8);
6513  -webkit-backdrop-filter: blur(20px) saturate(1.8);
6514  border: 1.5px solid var(--mm2-border-strong);
6515  border-radius: var(--mm2-radius-xl);
6516  box-shadow: var(--mm2-shadow-xl);
6517  display: none;
6518  overflow: hidden;
6519  opacity: 0;
6520  z-index: 500;
6521  transition: transform var(--mm2-transition-slow), opacity var(--mm2-transition-base);
6522}
6523.mm2-open .mm2-panel { 
6524  display: block; 
6525  opacity: 1; 
6526  transform: translateX(-50%) translateY(0);
6527}
6528
6529/* View switcher */
6530.mm2-view { display: none; }
6531.mm2-open .mm2-view.is-active { display: block; }
6532
6533/* Top grid (level-1 tiles) - Enhanced */
6534.mm2-top {
6535  padding: 24px;
6536  max-height: 75vh; overflow: auto;
6537}
6538.mm2-grid {
6539  display: grid;
6540  grid-template-columns: repeat(4, minmax(0,1fr));
6541  gap: 14px;
6542}
6543@media (max-width: 1200px) { .mm2-grid { grid-template-columns: repeat(3, minmax(0,1fr)); } }
6544@media (max-width: 960px)  { .mm2-grid { grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px; } }
6545
6546/* Stagger animation for cards */
6547.mm2-open .mm2-card {
6548  animation: slideInUp 0.3s cubic-bezier(0.4, 0, 0.2, 1) backwards;
6549}
6550.mm2-open .mm2-card:nth-child(1)  { animation-delay: 0.02s; }
6551.mm2-open .mm2-card:nth-child(2)  { animation-delay: 0.04s; }
6552.mm2-open .mm2-card:nth-child(3)  { animation-delay: 0.06s; }
6553.mm2-open .mm2-card:nth-child(4)  { animation-delay: 0.08s; }
6554.mm2-open .mm2-card:nth-child(5)  { animation-delay: 0.10s; }
6555.mm2-open .mm2-card:nth-child(6)  { animation-delay: 0.12s; }
6556.mm2-open .mm2-card:nth-child(7)  { animation-delay: 0.14s; }
6557.mm2-open .mm2-card:nth-child(8)  { animation-delay: 0.16s; }
6558.mm2-open .mm2-card:nth-child(9)  { animation-delay: 0.18s; }
6559.mm2-open .mm2-card:nth-child(10) { animation-delay: 0.20s; }
6560.mm2-open .mm2-card:nth-child(11) { animation-delay: 0.22s; }
6561.mm2-open .mm2-card:nth-child(12) { animation-delay: 0.24s; }
6562.mm2-open .mm2-card:nth-child(n+13) { animation-delay: 0.26s; }
6563
6564@keyframes slideInUp {
6565  from {
6566    opacity: 0;
6567    transform: translateY(12px);
6568  }
6569  to {
6570    opacity: 1;
6571    transform: translateY(0);
6572  }
6573}
6574
6575.mm2-card {
6576  position: relative;
6577  display: flex; gap: 14px; align-items: center;
6578  padding: 16px; border-radius: var(--mm2-radius-md);
6579  background: var(--mm2-surface);
6580  border: 1.5px solid var(--mm2-border);
6581  text-decoration: none; color: var(--mm2-fg);
6582  transition: all var(--mm2-transition-fast);
6583  min-height: 72px;
6584  box-shadow: var(--mm2-shadow-sm);
6585}
6586.mm2-card:hover { 
6587  border-color: var(--mm2-accent);
6588  background: linear-gradient(135deg, rgba(0,0,0,1), rgba(248,250,252,1));
6589  box-shadow: var(--mm2-shadow-hover);
6590  transform: translateY(-3px);
6591}
6592.mm2-card:active { transform: translateY(-1px); }
6593
6594/* Icon with category-specific gradients */
6595.mm2-card .mm2-ico {
6596  width: 48px; height: 48px; border-radius: 12px;
6597  display: inline-flex; align-items: center; justify-content: center;
6598  background: linear-gradient(135deg, #e0e7ff, #fff);
6599  border: 1.5px solid #c7d2fe;
6600  color: #4f46e5;
6601  flex-shrink: 0;
6602  transition: all var(--mm2-transition-fast);
6603  box-shadow: 0 2px 8px rgba(99,102,241,0.1);
6604}
6605.mm2-card:hover .mm2-ico {
6606  transform: scale(1.08) rotate(2deg);
6607  box-shadow: 0 4px 16px rgba(99,102,241,0.2);
6608}
6609.mm2-card .mm2-ico svg { transition: transform var(--mm2-transition-fast); }
6610.mm2-card:hover .mm2-ico svg { transform: scale(1.1); }
6611
6612.mm2-card .mm2-title { 
6613  font-weight: 700; 
6614  font-size: 15px;
6615  letter-spacing: -0.01em;
6616  line-height: 1.3;
6617}
6618.mm2-card .mm2-sub { 
6619  color: var(--mm2-fg-muted); 
6620  font-size: 12.5px; 
6621  margin-top: 3px;
6622  display: flex;
6623  align-items: center;
6624  gap: 4px;
6625  white-space: nowrap;
6626  overflow: hidden;
6627  text-overflow: ellipsis;
6628}
6629.mm2-card .mm2-count {
6630  display: inline-flex;
6631  padding: 2px 8px;
6632  border-radius: 6px;
6633  background: var(--mm2-accent-10);
6634  color: var(--mm2-accent);
6635  font-weight: 600;
6636  font-size: 11px;
6637  margin-left: 4px;
6638}
6639
6640/* Detail view (level-2 + level-3) - Enhanced */
6641.mm2-detail {
6642  position: relative;
6643  padding: 16px 24px 24px;
6644  max-height: 70vh; overflow: auto;
6645}
6646.mm2-detail-header {
6647  display: flex; align-items: center; gap: 14px;
6648  padding: 10px 0 16px; border-bottom: 2px solid var(--mm2-muted); margin-bottom: 16px;
6649}
6650.mm2-back {
6651  display: inline-flex; align-items: center; justify-content: center;
6652  width: 40px; height: 40px; border-radius: 12px;
6653  border: 1.5px solid var(--mm2-border); background: #fff; color: var(--mm2-fg);
6654  cursor: pointer;
6655  transition: all var(--mm2-transition-fast);
6656  box-shadow: var(--mm2-shadow-sm);
6657}
6658.mm2-back:hover {
6659  border-color: var(--mm2-accent);
6660  background: var(--mm2-muted);
6661  transform: translateX(-3px);
6662  box-shadow: var(--mm2-shadow-md);
6663}
6664.mm2-detail-title { 
6665  font-weight: 800; 
6666  font-size: 18px;
6667  letter-spacing: -0.02em;
6668}
6669
6670/* Two-pane layout (desktop) - Enhanced */
6671.mm2-detail-layout {
6672  display: grid;
6673  grid-template-columns: 280px 1fr;
6674  gap: 20px;
6675}
6676
6677/* Left: subnav (level-2 list) - Enhanced */
6678.mm2-subnav {
6679  position: sticky; top: 8px;
6680  align-self: start;
6681  max-height: calc(70vh - 56px);
6682  overflow: auto;
6683  padding-right: 6px;
6684}
6685.mm2-subnav::-webkit-scrollbar { width: 8px; height: 8px; }
6686.mm2-subnav::-webkit-scrollbar-track { background: transparent; }
6687.mm2-subnav::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 8px; }
6688.mm2-subnav::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
6689
6690.mm2-subitem {
6691  display: flex; align-items: center; justify-content: flex-start; gap: 10px;
6692  width: 100%;
6693  padding: 12px 14px;
6694  border-radius: 12px;
6695  border: 1.5px solid transparent;
6696  background: transparent;
6697  color: var(--mm2-fg);
6698  cursor: pointer;
6699  text-align: left;
6700  transition: all var(--mm2-transition-fast);
6701  margin-bottom: 6px;
6702  font-weight: 600;
6703  font-size: 14px;
6704  position: relative;
6705}
6706.mm2-subitem::before {
6707  content: '';
6708  position: absolute;
6709  left: 0;
6710  top: 50%;
6711  transform: translateY(-50%);
6712  width: 3px;
6713  height: 0;
6714  background: var(--mm2-accent);
6715  border-radius: 0 2px 2px 0;
6716  transition: height var(--mm2-transition-fast);
6717}
6718.mm2-subitem:hover { 
6719  background: var(--mm2-muted);
6720  color: var(--mm2-accent-hover);
6721}
6722.mm2-subitem.active {
6723  background: linear-gradient(135deg, rgba(99,102,241,0.08), rgba(99,102,241,0.04));
6724  border-color: var(--mm2-accent-20);
6725  color: var(--mm2-accent-hover);
6726  box-shadow: inset 0 0 0 1px rgba(99,102,241,0.1);
6727}
6728.mm2-subitem.active::before {
6729  height: 24px;
6730}
6731
6732/* Right: subcontent (level-3 grid) - Enhanced */
6733.mm2-subcontent { min-height: 240px; }
6734.mm2-links {
6735  display: grid; gap: 12px 14px;
6736  grid-template-columns: repeat(3, minmax(0,1fr));
6737}
6738@media (max-width: 1200px) { .mm2-links { grid-template-columns: repeat(2, minmax(0,1fr)); } }
6739
6740.mm2-link {
6741  display: flex; align-items: center; gap: 12px;
6742  padding: 14px; border: 1.5px solid var(--mm2-border); border-radius: var(--mm2-radius-md);
6743  background: var(--mm2-surface); color: var(--mm2-fg); text-decoration: none;
6744  min-height: 52px;
6745  font-weight: 500;
6746  font-size: 14px;
6747  transition: all var(--mm2-transition-fast);
6748  box-shadow: var(--mm2-shadow-sm);
6749  position: relative;
6750  justify-content: space-between;
6751}
6752.mm2-link:hover { 
6753  background: var(--mm2-muted); 
6754  border-color: var(--mm2-accent);
6755  transform: translateX(4px);
6756  box-shadow: var(--mm2-shadow-md);
6757}
6758.mm2-link-text {
6759  flex: 1;
6760}
6761
6762/* Product Thumbnails */
6763.mm2-thumbs {
6764  display: flex;
6765  gap: 6px;
6766  align-items: center;
6767  margin-left: auto;
6768  flex-shrink: 0;
6769}
6770.mm2-thumb {
6771  width: 36px;
6772  height: 36px;
6773  border-radius: 8px;
6774  object-fit: cover;
6775  border: 1.5px solid var(--mm2-border);
6776  box-shadow: 0 2px 6px rgba(0,0,0,0.08);
6777  transition: all var(--mm2-transition-fast);
6778  background: var(--mm2-muted);
6779}
6780.mm2-link:hover .mm2-thumb {
6781  border-color: var(--mm2-accent);
6782  box-shadow: 0 4px 10px rgba(99,102,241,0.15);
6783  transform: scale(1.08);
6784}
6785
6786/* Thumbnail Skeleton Loader */
6787.mm2-thumb-skeleton {
6788  width: 36px;
6789  height: 36px;
6790  border-radius: 8px;
6791  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
6792  background-size: 200% 100%;
6793  animation: shimmer 1.5s infinite;
6794  border: 1.5px solid var(--mm2-border);
6795}
6796
6797.mm2-badge {
6798  font-size: 11px; padding: 3px 10px; margin-left: auto;
6799  border-radius: 12px; background: var(--mm2-accent-10); color: var(--mm2-accent);
6800  font-weight: 700;
6801  border: 1px solid var(--mm2-accent-20);
6802}
6803
6804/* "See all" highlight card - Enhanced */
6805.mm2-seeall {
6806  border: 2px dashed var(--mm2-accent);
6807  background: linear-gradient(135deg, rgba(99,102,241,.08), rgba(99,102,241,.02));
6808  font-weight: 700;
6809}
6810.mm2-seeall:hover {
6811  background: linear-gradient(135deg, rgba(99,102,241,.14), rgba(99,102,241,.06));
6812  border-style: solid;
6813  transform: translateX(6px) scale(1.02);
6814}
6815
6816/* Mobile panel styles - Enhanced with drag handle & better touch targets */
6817@media (max-width: 960px) {
6818  .mm2-panel {
6819    position: fixed;
6820    left: 0;
6821    right: 0;
6822    bottom: 0;
6823    top: auto;
6824    width: 100%;
6825    max-height: 88vh;
6826    transform: translateX(0) translateY(100%);
6827    border-radius: var(--mm2-radius-xl) var(--mm2-radius-xl) 0 0;
6828    border-bottom: none;
6829  }
6830  .mm2-open .mm2-panel {
6831    transform: translateX(0) translateY(0);
6831 z-index: 9999;
6832  }
6833  
6834  /* Enhanced backdrop for mobile */
6835  .mm2-backdrop {
6836    backdrop-filter: blur(6px) saturate(1.2);
6837    -webkit-backdrop-filter: blur(6px) saturate(1.2);
6838  }
6839  
6840  /* Mobile close button - Soft glassmorphism */
6841  .mm2-mobile-close {
6842    position: absolute;
6843    top: 4px;
6844    right: 8px;
6845    width: 28px;
6846    height: 28px;
6847    border-radius: 50%;
6848    border: 1px solid rgba(226, 232, 240, 0.6);
6849    background: rgba(255, 255, 255, 0.85);
6850    backdrop-filter: blur(12px) saturate(1.8);
6851    -webkit-backdrop-filter: blur(12px) saturate(1.8);
6852    color: var(--mm2-fg-muted);
6853    display: inline-flex;
6854    align-items: center;
6855    justify-content: center;
6856    cursor: pointer;
6857    transition: all var(--mm2-transition-fast);
6858    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
6859    z-index: 20;
6860  }
6861  .mm2-mobile-close:hover {
6862    background: rgba(254, 242, 242, 0.95);
6863    border-color: rgba(252, 165, 165, 0.8);
6864    color: #dc2626;
6865    box-shadow: 0 4px 12px rgba(220, 38, 38, 0.15);
6866  }
6867  .mm2-mobile-close:active {
6868    transform: scale(0.92);
6869  }
6870  
6871  /* Drag handle indicator */
6872  .mm2-top::before,
6873  .mm2-detail::before {
6874    content: '';
6875    position: sticky;
6876    top: 0;
6877    left: 50%;
6878    transform: translateX(-50%);
6879    width: 40px;
6880    height: 4px;
6881    background: var(--mm2-border-strong);
6882    border-radius: 999px;
6883    margin: 12px auto 20px;
6884    display: block;
6885    z-index: 10;
6886    transition: background var(--mm2-transition-fast);
6887  }
6888  .mm2-panel:active .mm2-top::before,
6889  .mm2-panel:active .mm2-detail::before {
6890    background: #94a3b8;
6891  }
6892  
6893  .mm2-top {
6894    padding: 10px;  
6895  }
6896  
6897  .mm2-top, .mm2-detail { 
6898    max-height: min(82vh, 720px);
6899    padding-top: 8px;
6900  }
6901
6902  /* Two-pane collapses: subnav becomes GRID (no horizontal scroll) */
6903  .mm2-detail-layout { display: block; }
6904  .mm2-subnav {
6905    position: static; max-height: none; overflow: visible;
6906    display: grid; gap: 10px; padding: 10px 4px 16px; margin-bottom: 16px;
6907    grid-template-columns: repeat(2, minmax(0,1fr));
6908    border-bottom: 2px solid var(--mm2-muted);
6909  }
6910  .mm2-subitem {
6911    margin: 0; width: 100%; text-align: center;
6912    border-radius: 14px;
6913    background: #fff; border: 1.5px solid var(--mm2-border);
6914    white-space: normal; line-height: 1.3; min-height: 48px;
6915    justify-content: center;
6916    padding: 12px 10px;
6917    font-size: 13.5px;
6918    box-shadow: var(--mm2-shadow-sm);
6919  }
6920  .mm2-subitem::before { display: none; }
6921  .mm2-subitem:hover {
6922    transform: scale(1.02);
6923  }
6924  .mm2-subitem.active {
6925    background: linear-gradient(135deg, var(--mm2-accent), var(--mm2-accent-hover));
6926    border-color: var(--mm2-accent-hover);
6927    color: white;
6928    box-shadow: 0 4px 12px rgba(99,102,241,0.3);
6929    font-weight: 700;
6930  }
6931
6932  /* Level-3 grid tetap 2 kolom dengan spacing lebih baik */
6933  .mm2-links { 
6934    grid-template-columns: repeat(2, minmax(0,1fr));
6935    gap: 10px;
6936  }
6937  
6938  /* Mobile: hide thumbnails untuk save space */
6939  .mm2-thumbs {
6940    display: none;
6941  }
6942  
6943  /* Larger touch targets for mobile */
6944  .mm2-card {
6945    min-height: 80px;
6946    padding: 18px 14px;
6947  }
6948  .mm2-card .mm2-ico {
6949    width: 50px;
6950    height: 50px;
6951  }
6952  .mm2-card .mm2-title {
6953    font-size: 14px;
6954  }
6955  .mm2-card .mm2-sub {
6956    font-size: 11.5px;
6957  }
6958  .mm2-link {
6959    min-height: 56px;
6960    padding: 16px;
6961  }
6962  .mm2-back {
6963    width: 44px;
6964    height: 44px;
6965  }
6966  
6967  /* Grid spacing */
6968  .mm2-grid {
6969    gap: 10px;
6970  }
6971}
6972</style>
6973  <style data-inlined="product-compare.css">
6974/* Product Compare Feature Styles */
6975
6976/* Compare Button in Product Detail - Modern Chip Style */
6977.pd-compare-section {
6978  margin: 0.5rem 0;
6979  display: flex;
6980  align-items: center;
6981  gap: 0.5rem;
6982}
6983
6984.pd-compare-btn {
6985  display: inline-flex;
6986  align-items: center;
6987  gap: 0.4rem;
6988  padding: 0.5rem 0.85rem;
6989  border: 1px solid #cbd5e1;
6990  border-radius: 20px;
6991  background: #fff;
6992  color: #64748b;
6993  font-weight: 600;
6994  font-size: 0.8rem;
6995  cursor: pointer;
6996  transition: all 0.2s ease;
6997  position: relative;
6998  white-space: nowrap;
6999}
7000
7001.pd-compare-btn:hover {
7002  border-color: #3b82f6;
7003  color: #3b82f6;
7004  background: #f0f9ff;
7005  transform: translateY(-1px);
7006  box-shadow: 0 2px 6px rgba(59, 130, 246, 0.15);
7007}
7008
7009.pd-compare-btn.added {
7010  border-color: #16a34a;
7011  color: #16a34a;
7012  background: #f0fdf4;
7013}
7014
7015.pd-compare-btn .compare-remove {
7016  display: none;
7017}
7018
7019.pd-compare-btn.added .compare-text {
7020  display: none;
7021}
7022
7023.pd-compare-btn.added .compare-remove {
7024  display: flex !important;
7025  align-items: center;
7026  gap: 0.25rem;
7027  font-weight: 600;
7028  font-size: 0.9rem;
7029  color: #dc2626 !important;
7030  opacity: 1 !important;
7031  visibility: visible !important;
7032  z-index: 1;
7033  position: relative;
7034}
7035
7036.pd-compare-btn.added .compare-remove[hidden] {
7037  display: flex !important;
7038}
7039
7040/* Force visibility for added state */
7041.pd-compare-btn.added .compare-remove {
7042  display: flex !important;
7043}
7044
7045[hidden].compare-remove {
7046  display: none !important;
7047}
7048
7049.pd-compare-btn.added [hidden].compare-remove {
7050  display: flex !important;
7051}
7052
7053/* Ensure the added state text is always visible */
7054.pd-compare-btn.added {
7055  border-color: #dc2626 !important;
7056  color: #dc2626 !important;
7057  background: #fef2f2 !important;
7058}
7059
7060.pd-compare-btn.added .compare-remove svg {
7061  color: #dc2626 !important;
7062}
7063
7064.pd-compare-btn.added:hover {
7065  border-color: #15803d;
7066  color: #15803d;
7067  background: #dcfce7;
7068  transform: translateY(-1px);
7069  box-shadow: 0 2px 6px rgba(22, 163, 74, 0.2);
7070}
7071
7072.pd-compare-btn .compare-icon {
7073  width: 16px;
7074  height: 16px;
7075  flex-shrink: 0;
7076}
7077
7078.pd-compare-info {
7079  display: inline-flex;
7080  align-items: center;
7081}
7082
7083.compare-note {
7084  color: #94a3b8;
7085  font-size: 0.7rem;
7086  font-weight: 500;
7087  transition: color 0.2s ease;
7088}
7089
7090.pd-compare-btn.added + .pd-compare-info .compare-note {
7091  color: #16a34a;
7092  font-weight: 600;
7093}
7094
7095/* Compare Badge in Navigation */
7096.compare-indicator {
7097  position: relative;
7098}
7099
7100.compare-indicator .badge {
7101  position: absolute;
7102  top: -8px;
7103  right: -8px;
7104  min-width: 18px;
7105  height: 18px;
7106  padding: 0 5px;
7107  background: #ef4444;
7108  color: white;
7109  border-radius: 9px;
7110  font-size: 11px;
7111 
7112
7113/* Floating Compare Badge (Sticky) */
7114.compare-floating-badge {
7115  position: fixed;
7116  right: 16px;
7117  bottom: calc(20px + 60px); /* Above scroll-to-top button */
7118  width: auto;
7119  min-width: 56px;
7120  height: 56px;
7121  padding: 0 16px;
7122  border-radius: 28px;
7123  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
7124  color: #fff;
7125  box-shadow: 0 8px 24px -8px rgba(59, 130, 246, 0.5);
7126  cursor: pointer;
7127  z-index: 1104;
7128  opacity: 0;
7129  transform: translateY(8px) scale(0.9);
7130  transition: opacity 0.3s ease, transform 0.3s ease, box-shadow 0.2s ease;
7131  display: flex;
7132  align-items: center;
7133  justify-content: center;
7134  gap: 8px;
7135  font-weight: 600;
7136  font-size: 0.85rem;
7137  pointer-events: none;
7138}
7139
7140.compare-floating-badge.visible {
7141  opacity: 1;
7142  transform: translateY(0) scale(1);
7143  pointer-events: auto;
7144}
7145
7146.compare-floating-badge:hover {
7147  box-shadow: 0 12px 32px -10px rgba(59, 130, 246, 0.6);
7148  transform: translateY(-2px) scale(1.02);
7149}
7150
7151.compare-floating-badge:active {
7152  transform: translateY(0) scale(0.98);
7153}
7154
7155.compare-floating-badge .cfb-icon {
7156  width: 20px;
7157  height: 20px;
7158  flex-shrink: 0;
7159}
7160
7161.compare-floating-badge .cfb-count {
7162  font-size: 0.9rem;
7163  font-weight: 700;
7164  min-width: 20px;
7165  text-align: center;
7166  background: rgba(255, 255, 255, 0.25);
7167  padding: 2px 8px;
7168  border-radius: 12px;
7169  line-height: 1;
7170  display: flex;
7171  align-items: center;
7172  justify-content: center;
7173  height: 24px;
7174}
7175
7176.compare-floating-badge .cfb-text {
7177  display: none;
7178}
7179
7180@media (min-width: 641px) {
7181  .compare-floating-badge .cfb-text {
7182    display: block;
7183    white-space: nowrap;
7184  }
7185}
7186
7187@media (max-width: 640px) {
7188  .compare-floating-badge {
7189    right: 14px;
7190    bottom: calc(var(--scroll-top-bottom, calc(68px + 18px + env(safe-area-inset-bottom))) + 60px);
7191    min-width: 48px;
7192    height: 48px;
7193    padding: 0 12px;
7194    border-radius: 24px;
7195  }
7196  
7197  .compare-floating-badge .cfb-icon {
7198    width: 18px;
7199    height: 18px;
7200  }
7201  
7202  .compare-floating-badge .cfb-count {
7203    font-size: 0.8rem;
7204    padding: 2px 6px;
7205    height: 20px;
7206  }
7207} font-weight: 600;
7208  display: flex;
7209  align-items: center;
7210  justify-content: center;
7211  transform: scale(0);
7212  transition: transform 0.2s ease;
7213}
7214
7215.compare-indicator .badge.active {
7216  transform: scale(1);
7217}
7218
7219/* Compare Sidebar */
7220.compare-sidebar {
7221  position: fixed;
7222  top: 0;
7223  right: -400px;
7224  width: 400px;
7225  height: 100vh;
7226  background: #ffffff;
7227  box-shadow: -4px 0 20px rgba(0, 0, 0, 0.1);
7228  z-index: 9999;
7229  display: flex;
7230  flex-direction: column;
7231  transition: right 0.3s ease;
7232}
7233
7234.compare-sidebar.open {
7235  right: 0;
7236}
7237
7238.compare-overlay {
7239  position: fixed;
7240  top: 0;
7241  left: 0;
7242  width: 100%;
7243  height: 100%;
7244  background: rgba(0, 0, 0, 0.5);
7245  z-index: 9998;
7246  opacity: 0;
7247  visibility: hidden;
7248  transition: all 0.3s ease;
7249}
7250
7251.compare-overlay.active {
7252  opacity: 1;
7253  visibility: visible;
7254}
7255
7256.compare-header {
7257  display: flex;
7258  align-items: center;
7259  justify-content: space-between;
7260  padding: 1.25rem;
7261  border-bottom: 1px solid #e2e8f0;
7262  background: #f8fafc;
7263}
7264
7265.compare-title {
7266  display: flex;
7267  align-items: center;
7268  gap: 0.5rem;
7269  font-size: 1rem;
7270  font-weight: 600;
7271  color: #1e293b;
7272  margin: 0;
7273}
7274
7275.compare-count {
7276  color: #64748b;
7277  font-weight: 400;
7278}
7279
7280.compare-close {
7281  display: flex;
7282  align-items: center;
7283  justify-content: center;
7284  width: 32px;
7285  height: 32px;
7286  border: none;
7287  border-radius: 6px;
7288  background: transparent;
7289  color: #64748b;
7290  cursor: pointer;
7291  transition: all 0.2s ease;
7292}
7293
7294.compare-close:hover {
7295  background: #e2e8f0;
7296  color: #374151;
7297}
7298
7299.compare-content {
7300  flex: 1;
7301  overflow-y: auto;
7302  padding: 1rem;
7303}
7304
7305.compare-empty {
7306  display: flex;
7307  flex-direction: column;
7308  align-items: center;
7309  justify-content: center;
7310  height: 100%;
7311  text-align: center;
7312  padding: 2rem 1rem;
7313}
7314
7315.compare-empty h5 {
7316  margin: 1rem 0 0.5rem;
7317  color: #374151;
7318}
7319
7320.compare-empty p {
7321  color: #64748b;
7322  font-size: 0.875rem;
7323  line-height: 1.5;
7324}
7325
7326.compare-list {
7327  display: flex;
7328  flex-direction: column;
7329  gap: 0.75rem;
7330}
7331
7332.compare-item {
7333  display: flex;
7334  align-items: center;
7335  gap: 0.75rem;
7336  padding: 0.75rem;
7337  border: 1px solid #e2e8f0;
7338  border-radius: 8px;
7339  background: #ffffff;
7340  transition: all 0.2s ease;
7341}
7342
7343.compare-item:hover {
7344  border-color: #cbd5e1;
7345  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
7346}
7347
7348.compare-item-image {
7349  flex-shrink: 0;
7350  width: 48px;
7351  height: 48px;
7352  border-radius: 6px;
7353  overflow: hidden;
7354  border: 1px solid #e2e8f0;
7355}
7356
7357.compare-item-image img {
7358  width: 100%;
7359  height: 100%;
7360  object-fit: cover;
7361}
7362
7363.compare-item-info {
7364  flex: 1;
7365  min-width: 0;
7366}
7367
7368.compare-item-name {
7369  font-weight: 500;
7370  font-size: 0.875rem;
7371  color: #1e293b;
7372  margin-bottom: 0.25rem;
7373  line-height: 1.3;
7374  display: -webkit-box;
7375  -webkit-line-clamp: 2;
7376  -webkit-box-orient: vertical;
7377  overflow: hidden;
7378}
7379
7380.compare-item-price {
7381  font-weight: 600;
7382  font-size: 0.8rem;
7383  color: #059669;
7384}
7385
7386.compare-item-remove {
7387  flex-shrink: 0;
7388  display: flex;
7389  align-items: center;
7390  justify-content: center;
7391  width: 28px;
7392  height: 28px;
7393  border: none;
7394  border-radius: 4px;
7395  background: transparent;
7396  color: #dc2626;
7397  cursor: pointer;
7398  transition: all 0.2s ease;
7399}
7400
7401.compare-item-remove:hover {
7402  background: #fef2f2;
7403  color: #991b1b;
7404}
7405
7406.compare-actions {
7407  padding: 1rem;
7408  border-top: 1px solid #e2e8f0;
7409  display: flex;
7410  gap: 0.75rem;
7411}
7412
7413.compare-actions .btn {
7414  flex: 1;
7415  padding: 0.75rem 1rem;
7416  border-radius: 6px;
7417  font-weight: 600;
7418  font-size: 0.875rem;
7419  display: flex;
7420  align-items: center;
7421  justify-content: center;
7422  gap: 0.5rem;
7423  cursor: pointer;
7424  transition: all 0.2s ease;
7425}
7426
7427.btn-outline-danger {
7428  border: 1px solid #dc2626;
7429  background: transparent;
7430  color: #dc2626;
7431}
7432
7433.btn-outline-danger:hover {
7434  background: #dc2626;
7435  color: white;
7436}
7437
7438.btn-primary {
7439  border: 1px solid #3b82f6;
7440  background: #3b82f6;
7441  color: white;
7442}
7443
7444.btn-primary:hover {
7445  background: #2563eb;
7446  border-color: #2563eb;
7447}
7448
7449.btn-primary:disabled {
7450  opacity: 0.5;
7451  cursor: not-allowed;
7452}
7453
7454.btn-primary:disabled:hover {
7455  background: #3b82f6;
7456  border-color: #3b82f6;
7457}
7458
7459/* AI Comparison Modal */
7460.ai-compare-modal {
7461  position: fixed;
7462  top: 0;
7463  left: 0;
7464  width: 100%;
7465  height: 100%;
7466  z-index: 10000;
7467  display: none;
7468  align-items: center;
7469  justify-content: center;
7470}
7471
7472.ai-compare-modal.open {
7473  display: flex;
7474}
7475
7476.ai-compare-modal .modal-backdrop {
7477  position: absolute;
7478  top: 0;
7479  left: 0;
7480  width: 100%;
7481  height: 100%;
7482  background: rgba(0, 0, 0, 0.6);
7483  backdrop-filter: blur(4px);
7484}
7485
7486.modal-container {
7487  position: relative;
7488  width: 90%;
7489  max-width: 1000px;
7490  max-height: 90vh;
7491  background: #ffffff;
7492  border-radius: 12px;
7493  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
7494  display: flex;
7495  flex-direction: column;
7496  overflow: hidden;
7497}
7498
7499.modal-header {
7500  display: flex;
7501  align-items: center;
7502  justify-content: space-between;
7503  padding: 1.5rem 2rem;
7504  border-bottom: 1px solid #e2e8f0;
7505  background: #f8fafc;
7506}
7507
7508.modal-title {
7509  display: flex;
7510  align-items: center;
7511  gap: 0.75rem;
7512  font-size: 1.25rem;
7513  font-weight: 600;
7514  color: #1e293b;
7515  margin: 0;
7516}
7517
7518.modal-close {
7519  display: flex;
7520  align-items: center;
7521  justify-content: center;
7522  width: 36px;
7523  height: 36px;
7524  border: none;
7525  border-radius: 8px;
7526  background: transparent;
7527  color: #64748b;
7528  cursor: pointer;
7529  transition: all 0.2s ease;
7530}
7531
7532.modal-close:hover {
7533  background: #e2e8f0;
7534  color: #374151;
7535}
7536
7537.modal-content {
7538  flex: 1;
7539  overflow-y: auto;
7540  padding: 2rem;
7541}
7542
7543/* AI Loading State */
7544.ai-loading {
7545  display: flex;
7546  flex-direction: column;
7547  align-items: center;
7548  justify-content: center;
7549  padding: 3rem 2rem;
7550  text-align: center;
7551}
7552
7553.loading-animation {
7554  margin-bottom: 1rem;
7555}
7556
7557.typing-indicator {
7558  display: flex;
7559  align-items: center;
7560  justify-content: center;
7561  gap: 4px;
7562  margin-bottom: 1rem;
7563}
7564
7565.typing-indicator span {
7566  width: 8px;
7567  height: 8px;
7568  background: #3b82f6;
7569  border-radius: 50%;
7570  animation: typing 1.4s infinite ease-in-out;
7571}
7572
7573.typing-indicator span:nth-child(2) {
7574  animation-delay: 0.2s;
7575}
7576
7577.typing-indicator span:nth-child(3) {
7578  animation-delay: 0.4s;
7579}
7580
7581@keyframes typing {
7582  0%, 80%, 100% {
7583    opacity: 0.3;
7584    transform: scale(0.8);
7585  }
7586  40% {
7587    opacity: 1;
7588    transform: scale(1);
7589  }
7590}
7591
7592.ai-loading p {
7593  font-weight: 500;
7594  color: #1e293b;
7595  margin-bottom: 0.5rem;
7596}
7597
7598.ai-loading small {
7599  color: #64748b;
7600}
7601
7602/* AI Error State */
7603.ai-error {
7604  display: flex;
7605  flex-direction: column;
7606  align-items: center;
7607  justify-content: center;
7608  padding: 3rem 2rem;
7609  text-align: center;
7610}
7611
7612.ai-error h4 {
7613  margin: 1rem 0 0.5rem;
7614  color: #dc2626;
7615}
7616
7617.ai-error p {
7618  color: #64748b;
7619  margin-bottom: 1.5rem;
7620}
7621
7622/* AI Results */
7623.ai-results > div {
7624  margin-bottom: 2rem;
7625}
7626
7627.ai-summary-card,
7628.ai-comparison-table,
7629.ai-detailed-analysis,
7630.ai-recommendations,
7631.ai-tips {
7632  background: #f8fafc;
7633  border: 1px solid #e2e8f0;
7634  border-radius: 12px;
7635  padding: 1.5rem;
7636}
7637
7638.ai-results h4 {
7639  display: flex;
7640  align-items: center;
7641  gap: 0.5rem;
7642  font-size: 1.1rem;
7643  font-weight: 600;
7644  color: #1e293b;
7645  margin-bottom: 1rem;
7646}
7647
7648.summary-text {
7649  font-size: 1rem;
7650  line-height: 1.6;
7651  color: #374151;
7652  margin-bottom: 1rem;
7653}
7654
7655.winner-badge {
7656  padding: 1rem;
7657  background: linear-gradient(135deg, #10b981 0%, #059669 100%);
7658  color: white;
7659  border-radius: 8px;
7660}
7661
7662.winner-badge strong {
7663  display: block;
7664  margin-bottom: 0.25rem;
7665}
7666
7667.winner-badge small {
7668  opacity: 0.9;
7669  font-size: 0.875rem;
7670}
7671
7672/* Comparison Table */
7673.comparison-grid {
7674  display: grid;
7675  gap: 1rem;
7676  margin-top: 1rem;
7677}
7678
7679.comparison-category {
7680  background: white;
7681  border: 1px solid #e2e8f0;
7682  border-radius: 8px;
7683  overflow: hidden;
7684}
7685
7686.category-header {
7687  background: #f1f5f9;
7688  padding: 0.75rem 1rem;
7689  font-weight: 600;
7690  color: #1e293b;
7691  border-bottom: 1px solid #e2e8f0;
7692}
7693
7694.category-products {
7695  display: grid;
7696  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
7697  gap: 1px;
7698  background: #e2e8f0;
7699}
7700
7701.product-score {
7702  background: white;
7703  padding: 1rem;
7704  text-align: center;
7705}
7706
7707.score-value {
7708  display: inline-flex;
7709  align-items: center;
7710  gap: 0.25rem;
7711  margin-bottom: 0.5rem;
7712}
7713
7714.score-stars {
7715  color: #fbbf24;
7716}
7717
7718.score-note {
7719  font-size: 0.8rem;
7720  color: #64748b;
7721  line-height: 1.3;
7722}
7723
7724/* Product name header in comparison table */
7725.product-name-header {
7726  margin-bottom: 0.5rem;
7727}
7728
7729.product-name-header strong {
7730  font-size: .8rem; /* h5 size (18px) */
7731  font-weight: 600;
7732  color: #1e293b;
7733  line-height: 1.3;
7734  display: block;
7735  word-break: break-word;
7736}
7737
7738.product-price {
7739  font-size: 0.8rem;
7740  font-weight: 600;
7741  color: #059669;
7742}
7743
7744/* Detailed Analysis */
7745.products-analysis {
7746  display: grid;
7747  gap: 1rem;
7748  margin-top: 1rem;
7749}
7750
7751.product-analysis {
7752  background: white;
7753  border: 1px solid #e2e8f0;
7754  border-radius: 8px;
7755  padding: 1rem;
7756}
7757
7758.product-analysis h5 {
7759  color: #1e293b;
7760  margin-bottom: 0.75rem;
7761  font-weight: 600;
7762}
7763
7764.analysis-section {
7765  margin-bottom: 0.75rem;
7766}
7767
7768.analysis-section:last-child {
7769  margin-bottom: 0;
7770}
7771
7772.analysis-section h6 {
7773  font-size: 0.875rem;
7774  font-weight: 600;
7775  margin-bottom: 0.5rem;
7776}
7777
7778.analysis-section h6.strengths {
7779  color: #059669;
7780}
7781
7782.analysis-section h6.weaknesses {
7783  color: #dc2626;
7784}
7785
7786.analysis-section h6.best-for {
7787  color: #7c3aed;
7788}
7789
7790.analysis-section ul {
7791  list-style: none;
7792  padding: 0;
7793  margin: 0;
7794}
7795
7796.analysis-section li {
7797  display: flex;
7798  align-items: flex-start;
7799  gap: 0.5rem;
7800  margin-bottom: 0.25rem;
7801  font-size: 0.875rem;
7802  line-height: 1.4;
7803}
7804
7805.analysis-section li:before {
7806  content: "•";
7807  color: #64748b;
7808  font-weight: bold;
7809  margin-top: 0.1rem;
7810}
7811
7812.best-for-text {
7813  font-size: 0.875rem;
7814  color: #374151;
7815  font-style: italic;
7816}
7817
7818/* Recommendations */
7819.recommendations-grid {
7820  display: grid;
7821  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
7822  gap: 1rem;
7823  margin-top: 1rem;
7824}
7825
7826.recommendation-card {
7827  background: white;
7828  border: 1px solid #e2e8f0;
7829  border-radius: 8px;
7830  padding: 1rem;
7831  text-align: center;
7832}
7833
7834.recommendation-card h5 {
7835  font-size: 1rem;
7836  margin-bottom: 0.75rem;
7837  color: #1e293b;
7838}
7839
7840.recommendation-card p {
7841  font-size: 0.875rem;
7842  color: #374151;
7843  line-height: 1.5;
7844}
7845
7846/* Tips */
7847.tips-list {
7848  list-style: none;
7849  padding: 0;
7850  margin: 1rem 0 0 0;
7851}
7852
7853.tips-list li {
7854  display: flex;
7855  align-items: flex-start;
7856  gap: 0.75rem;
7857  margin-bottom: 0.75rem;
7858  font-size: 0.9rem;
7859  line-height: 1.5;
7860  color: #374151;
7861}
7862
7863.tips-list li:before {
7864  content: "💡";
7865  font-size: 1rem;
7866  margin-top: 0.1rem;
7867}
7868
7869/* Modal Footer */
7870.modal-footer {
7871  display: flex;
7872  justify-content: space-between;
7873  align-items: center;
7874  padding: 1.5rem 2rem;
7875  border-top: 1px solid #e2e8f0;
7876  background: #f8fafc;
7877}
7878
7879.btn-outline-secondary {
7880  border: 1px solid #64748b;
7881  background: transparent;
7882  color: #64748b;
7883  padding: 0.75rem 1.5rem;
7884  border-radius: 6px;
7885  font-weight: 600;
7886  display: flex;
7887  align-items: center;
7888  gap: 0.5rem;
7889  cursor: pointer;
7890  transition: all 0.2s ease;
7891}
7892
7893.btn-outline-secondary:hover {
7894  background: #64748b;
7895  color: white;
7896}
7897
7898/* Responsive for chip button */
7899@media (max-width: 880px) {
7900  .pd-compare-section {
7901    margin: 1rem 0;
7902    display: block;
7903  }
7904
7905  .pd-compare-btn {
7906    width: 100%;
7907    font-size: 0.85rem;
7908    padding: 0.65rem 1rem;
7909    border-radius: 10px;
7910    justify-content: center;
7911    min-height: 44px;
7912  }
7913  
7914  .pd-compare-btn .compare-icon {
7915    width: 16px;
7916    height: 16px;
7917  }
7918
7919  .pd-compare-info {
7920    margin-top: 0.5rem;
7921    display: block;
7922    text-align: center;
7923  }
7924
7925  .compare-note {
7926    font-size: 0.7rem;
7927    display: block;
7928  }
7929}
7930
7931/* Responsive Design */
7932@media (max-width: 768px) {
7933  .compare-sidebar {
7934    width: 100%;
7935    right: -100%;
7936    /* Fix for mobile viewport height changes */
7937    height: 100vh;
7938    height: 100dvh; /* Dynamic viewport height for modern browsers */
7939  }
7940  
7941  .compare-actions {
7942    /* Ensure actions are always visible */
7943    position: sticky;
7944    bottom: 0;
7945    background: white;
7946    border-top: 2px solid #e2e8f0;
7947    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
7948    z-index: 10;
7949  }
7950  
7951  .compare-actions .btn {
7952    /* Larger touch targets for mobile */
7953    min-height: 48px;
7954    font-size: 0.9rem;
7955    font-weight: 700;
7956  }
7957  
7958  .modal-container {
7959    width: 95%;
7960    max-height: 95vh;
7961    max-height: 95dvh; /* Dynamic viewport height */
7962  }
7963  
7964  .modal-header,
7965  .modal-content,
7966  .modal-footer {
7967    padding: 1rem;
7968  }
7969  
7970  .recommendations-grid {
7971    grid-template-columns: 1fr;
7972  }
7973  
7974  .category-products {
7975    grid-template-columns: 1fr;
7976  }
7977  
7978  .modal-footer {
7979    flex-direction: column;
7980    gap: 0.75rem;
7981  }
7982  
7983  .modal-footer .btn {
7984    width: 100%;
7985  }
7986  
7987  /* Ensure compare button is visible on mobile */
7988  .pd-compare-section {
7989    display: block !important;
7990    visibility: visible !important;
7991    opacity: 1 !important;
7992    margin: 1rem 0;
7993    padding: 0;
7994    position: relative !important;
7995    z-index: 1 !important;
7996    max-width: 100%;
7997    overflow: hidden;
7998  }
7999  
8000  .pd-compare-btn {
8001    display: flex !important;
8002    visibility: visible !important;
8003    opacity: 1 !important;
8004    width: 100%;
8005    padding: 0.75rem 1rem;
8006    font-size: 0.9rem;
8007    min-height: 48px; /* Minimum touch target for mobile */
8008    border: 2px solid #e2e8f0 !important;
8009    background: #fff !important;
8010    position: relative !important;
8011    z-index: 2 !important;
8012    box-sizing: border-box;
8013    white-space: normal !important; /* Allow text wrapping on mobile */
8014    flex-wrap: wrap;
8015    gap: 0.5rem;
8016  }
8017  
8018  .pd-compare-btn .compare-text {
8019    display: inline !important;
8020    visibility: visible !important;
8021    font-weight: 600;
8022    color: #475569 !important;
8023  }
8024  
8025  .pd-compare-btn .compare-remove {
8026    display: none !important; /* Hidden by default */
8027  }
8028  
8029  .pd-compare-btn.added .compare-remove {
8030    display: flex !important; /* Only show when added */
8031    flex-wrap: wrap;
8032    align-items: center;
8033    gap: 0.4rem;
8034    white-space: normal !important;
8035    word-break: break-word;
8036  }
8037  
8038  .pd-compare-btn .compare-icon {
8039    display: inline-block !important;
8040    width: 18px;
8041    height: 18px;
8042    flex-shrink: 0;
8043  }
8044  
8045  .pd-compare-info {
8046    margin-top: 0.5rem;
8047  }
8048  
8049  .compare-note {
8050    font-size: 0.85rem !important;
8051    display: block !important;
8052    visibility: visible !important;
8053    color: #64748b !important;
8054  }
8055}
8056
8057/* iOS Safari specific fixes */
8058@media (max-width: 480px) {
8059  #compareBtn { 
8060    width:46px;
8061    height:46px;
8062  }
8063  
8064  .pd-compare-section {
8065    -webkit-transform: translateZ(0);
8066    transform: translateZ(0);
8067  }
8068  
8069  .pd-compare-btn {
8070    -webkit-appearance: none !important;
8071    appearance: none !important;
8072    -webkit-tap-highlight-color: transparent;
8073    touch-action: manipulation;
8074  }
8075  
8076  /* Fix sidebar height for iOS address bar */
8077  .compare-sidebar {
8078    /* Use fixed positioning that accounts for iOS address bar */
8079    height: 100vh;
8080    height: -webkit-fill-available; /* iOS Safari compatibility */
8081    min-height: 100vh;
8082    min-height: -webkit-fill-available;
8083  }
8084  
8085  /* Ensure compare actions stay at bottom */
8086  .compare-actions {
8087    position: sticky;
8088    bottom: 0;
8089    background: white;
8090    border-top: 2px solid #e2e8f0;
8091    box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.15);
8092    padding: 1.25rem 1rem;
8093    z-index: 20;
8094  }
8095  
8096  .compare-actions .btn {
8097    min-height: 52px; /* Larger for easier tapping */
8098    font-size: 1rem;
8099    font-weight: 700;
8100    border-radius: 8px;
8101  }
8102  
8103  /* Adjust content padding to account for sticky actions */
8104  .compare-content {
8105    padding-bottom: 6rem; /* Extra space for sticky actions */
8106  }
8107}
8108
8109/* High-resolution mobile devices (iPhone 12 Pro, etc.) */
8110@media (-webkit-min-device-pixel-ratio: 2) and (max-width: 430px) {
8111  .pd-compare-section {
8112    display: block !important;
8113  }
8114  
8115  .pd-compare-btn {
8116    display: flex !important;
8117    min-height: 44px;
8118    font-size: 16px; /* Prevent zoom on iOS */
8119  }
8120  
8121  /* Enhanced fixes for iPhone 12 Pro and similar devices */
8122  .compare-sidebar {
8123    /* Ensure full height coverage */
8124    height: 100vh;
8125    height: 100dvh; /* Dynamic viewport height */
8126    height: -webkit-fill-available; /* iOS fallback */
8127    max-height: 100vh;
8128    max-height: 100dvh;
8129    max-height: -webkit-fill-available;
8130    overflow: hidden; /* Prevent scroll issues */
8131  }
8132  
8133  .compare-content {
8134    /* Flexible content area */
8135    flex: 1;
8136    overflow-y: auto;
8137    -webkit-overflow-scrolling: touch;
8138    padding: 1rem 1rem 8rem 1rem; /* Extra bottom padding */
8139  }
8140  
8141  .compare-actions {
8142    /* Force actions to stay visible */
8143    position: fixed;
8144    bottom: 0;
8145    left: 0;
8146    right: 0;
8147    width: 100%;
8148    background: white;
8149    border-top: 2px solid #e2e8f0;
8150    box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.2);
8151    padding: 1rem;
8152    z-index: 9999;
8153    display: flex !important;
8154    gap: 0.75rem;
8155  }
8156  
8157  .compare-actions .btn {
8158    flex: 1;
8159    min-height: 30px; /* Extra large for high-DPI screens */
8160    font-size: .8rem;
8161    font-weight: 600;
8162    border-radius: 12px;
8163    display: flex !important;
8164    align-items: center;
8165    justify-content: center;
8166    gap: 0.5rem;
8167  }
8168  
8169  /* Ensure AI analyze button is always visible and prominent */
8170  #compareAnalyze {
8171    background: #3b82f6 !important;
8172    border-color: #3b82f6 !important;
8173    color: white !important;
8174    box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
8175  }
8176  
8177  #compareAnalyze:not(:disabled) {
8178    background: #2563eb !important;
8179    border-color: #2563eb !important;
8180    transform: scale(1.02);
8181  }
8182  
8183  #compareAnalyze:disabled {
8184    opacity: 0.6 !important;
8185    background: #9ca3af !important;
8186    border-color: #9ca3af !important;
8187  }
8188}
8189
8190/* AI Disclaimer Styles */
8191.ai-disclaimer {
8192  display: flex;
8193  align-items: flex-start;
8194  gap: 0.75rem;
8195  padding: 1rem;
8196  background: #fef3c7;
8197  border: 1px solid #fbbf24;
8198  border-radius: 8px;
8199  margin: 0;
8200}
8201
8202.disclaimer-icon {
8203  font-size: 1.25rem;
8204  flex-shrink: 0;
8205  margin-top: 0.125rem;
8206}
8207
8208.disclaimer-text {
8209  font-size: 0.85rem;
8210  line-height: 1.4;
8211  color: #92400e;
8212}
8213
8214.disclaimer-text strong {
8215  color: #78350f;
8216  font-weight: 600;
8217}
8218
8219/* Compare Disclaimer in Sidebar */
8220.compare-disclaimer {
8221  margin-top: 1rem;
8222  padding: 1rem;
8223  background: #f0f9ff;
8224  border: 1px solid #0ea5e9;
8225  border-radius: 8px;
8226}
8227
8228.compare-disclaimer h6 {
8229  display: flex;
8230  align-items: center;
8231  gap: 0.5rem;
8232  font-size: 0.875rem;
8233  font-weight: 600;
8234  color: #0c4a6e;
8235  margin-bottom: 0.5rem;
8236}
8237
8238.compare-disclaimer p {
8239  font-size: 0.8rem;
8240  line-height: 1.4;
8241  color: #075985;
8242  margin-bottom: 0.5rem;
8243}
8244
8245.compare-disclaimer p:last-child {
8246  margin-bottom: 0;
8247}
8248
8249.compare-disclaimer ul {
8250  list-style: none;
8251  padding: 0;
8252  margin: 0.5rem 0 0 0;
8253}
8254
8255.compare-disclaimer li {
8256  display: flex;
8257  align-items: flex-start;
8258  gap: 0.5rem;
8259  margin-bottom: 0.25rem;
8260  font-size: 0.75rem;
8261  line-height: 1.3;
8262  color: #075985;
8263}
8264
8265.compare-disclaimer li:before {
8266  content: "•";
8267  color: #0ea5e9;
8268  font-weight: bold;
8269  margin-top: 0.1rem;
8270  flex-shrink: 0;
8271}
8272
8273/* Mobile Browser Address Bar Fixes */
8274@supports (height: 100dvh) {
8275  /* Modern browsers with dynamic viewport support */
8276  @media (max-width: 768px) {
8277    .compare-sidebar {
8278      height: 100dvh !important;
8279      max-height: 100dvh !important;
8280    }
8281    
8282    .modal-container {
8283      max-height: 95dvh !important;
8284    }
8285  }
8286}
8287
8288@supports (height: -webkit-fill-available) {
8289  /* iOS Safari specific support */
8290  @media (max-width: 768px) {
8291    .compare-sidebar {
8292      height: -webkit-fill-available !important;
8293      max-height: -webkit-fill-available !important;
8294    }
8295  }
8296}
8297
8298/* Fallback for older browsers */
8299@media (max-width: 768px) and (orientation: portrait) {
8300  .compare-sidebar {
8301    /* Fixed height that works across different mobile browsers */
8302    min-height: 600px;
8303    height: calc(100vh - env(keyboard-inset-height, 0px));
8304    height: calc(100vh - env(safe-area-inset-bottom, 0px));
8305  }
8306  
8307  /* Ensure actions are always accessible */
8308  .compare-actions {
8309    position: sticky !important;
8310    bottom: env(safe-area-inset-bottom, 0px) !important;
8311    margin-bottom: 0 !important;
8312    padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0px)) !important;
8313  }
8314}
8315
8316/* PWA and fullscreen app support */
8317@media (display-mode: standalone) {
8318  .compare-sidebar {
8319    height: 100vh !important;
8320    height: 100dvh !important;
8321  }
8322}
8323
8324/* Utilities */
8325.btn-sm {
8326  padding: 0.5rem 0.75rem;
8327  font-size: 0.8rem;
8328}
8329
8330[hidden] {
8331  display: none !important;
8332}
8333
8334/* ===== UX ENHANCEMENTS ===== */
8335
8336/* Compare Button Highlight Animation */
8337.pulse-highlight {
8338  animation: pulse-glow 2s ease-in-out 3;
8339  box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7);
8340}
8341
8342@keyframes pulse-glow {
8343  0% {
8344    box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7);
8345  }
8346  50% {
8347    box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
8348  }
8349  100% {
8350    box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
8351  }
8352}
8353
8354/* Flying Product Animation */
8355.flying-product {
8356  position: fixed;
8357  z-index: 9999;
8358  pointer-events: none;
8359}
8360
8361.flying-product img {
8362  border-radius: 8px;
8363  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
8364}
8365
8366/* Sticky Compare Bar - REMOVED (using only fly-to-navbar animation) */
8367
8368/* Inline Compare Preview - REMOVED (keeping it simple and clean) */
8369</style>
8370  <style data-inlined="product-card-actions.css">
8371/**
8372 * Product Card Quick Actions - 3 Dot Menu + Bottom Sheet
8373 * Tokopedia-inspired design
8374 */
8375
8376/* ===== 3 Dot Button on Product Card ===== */
8377.prod-card {
8378    position: relative;
8379}
8380
8381.prod-card-actions-btn {
8382    position: absolute;
8383    top: 8px;
8384    right: 8px;
8385    width: 32px;
8386    height: 32px;
8387    border-radius: 50%;
8388    background: rgba(255, 255, 255, 0.95);
8389    border: 1px solid rgba(0, 0, 0, 0.08);
8390    display: inline-flex;
8391    align-items: center;
8392    justify-content: center;
8393    cursor: pointer;
8394    z-index: 3;
8395    transition: all 0.2s ease;
8396    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
8397    -webkit-tap-highlight-color: transparent;
8398}
8399
8400.prod-card-actions-btn:hover {
8401    background: #fff;
8402    border-color: rgba(0, 0, 0, 0.15);
8403    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
8404    transform: scale(1.05);
8405}
8406
8407.prod-card-actions-btn:active {
8408    transform: scale(0.95);
8409}
8410
8411.prod-card-actions-btn svg {
8412    width: 20px;
8413    height: 20px;
8414    color: #64748b;
8415    display: block;
8416}
8417
8418/* Desktop: Show on hover */
8419@media (min-width: 641px) {
8420    .prod-card-actions-btn {
8421        opacity: 0;
8422        pointer-events: none;
8423    }
8424    
8425    .prod-card:hover .prod-card-actions-btn {
8426        opacity: 1;
8427        pointer-events: auto;
8428    }
8429}
8430
8431/* Mobile: Always visible but smaller */
8432@media (max-width: 640px) {
8433    .prod-card-actions-btn {
8434        width: 28px;
8435        height: 28px;
8436        top: 6px;
8437        right: 6px;
8438        opacity: 95%;
8439    }
8440    
8441    .prod-card-actions-btn svg {
8442        width: 18px;
8443        height: 18px;
8444    }
8445}
8446
8447/* ===== Bottom Sheet Overlay ===== */
8448.pca-overlay {
8449    position: fixed;
8450    inset: 0;
8451    background: rgba(15, 23, 42, 0.6);
8452    backdrop-filter: blur(4px);
8453    z-index: 99998;
8454    opacity: 0;
8455    visibility: hidden;
8456    transition: all 0.3s ease;
8457}
8458
8459.pca-overlay.active {
8460    opacity: 1;
8461    visibility: visible;
8462}
8463
8464/* ===== Bottom Sheet Container ===== */
8465.pca-bottom-sheet {
8466    position: fixed;
8467    bottom: 0;
8468    left: 0;
8469    right: 0;
8470    background: #fff;
8471    border-radius: 20px 20px 0 0;
8472    box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.15);
8473    z-index: 99999;
8474    max-width: 600px;
8475    margin: 0 auto;
8476    transform: translateY(100%);
8477    transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
8478}
8479
8480.pca-bottom-sheet.active {
8481    transform: translateY(0);
8482}
8483
8484/* Drag Handle */
8485.pca-sheet-handle {
8486    width: 40px;
8487    height: 4px;
8488    background: #cbd5e1;
8489    border-radius: 999px;
8490    margin: 12px auto 8px;
8491}
8492
8493/* Product Preview */
8494.pca-sheet-product {
8495    display: flex;
8496    gap: 12px;
8497    padding: 12px 20px;
8498    border-bottom: 1px solid #f1f5f9;
8499}
8500
8501.pca-product-image {
8502    width: 60px;
8503    height: 60px;
8504    border-radius: 8px;
8505    object-fit: cover;
8506    flex-shrink: 0;
8507    border: 1px solid #e2e8f0;
8508}
8509
8510.pca-product-info {
8511    flex: 1;
8512    min-width: 0;
8513}
8514
8515.pca-product-name {
8516    font-size: 14px;
8517    font-weight: 600;
8518    color: #0f172a;
8519    margin: 0 0 4px 0;
8520    display: -webkit-box;
8521    -webkit-line-clamp: 2;
8522    -webkit-box-orient: vertical;
8523    overflow: hidden;
8524    line-height: 1.3;
8525}
8526
8527.pca-product-price {
8528    font-size: 15px;
8529    font-weight: 700;
8530    color: #0ea5e9;
8531    margin: 0;
8532}
8533
8534.pca-product-stock {
8535    font-size: 12px;
8536    color: #64748b;
8537    margin: 2px 0 0 0;
8538}
8539
8540.pca-product-stock.in {
8541    color: #16a34a;
8542}
8543
8544.pca-product-stock.low {
8545    color: #ea580c;
8546}
8547
8548.pca-product-stock.out {
8549    color: #dc2626;
8550}
8551
8552/* Action Menu */
8553.pca-sheet-actions {
8554    padding: 8px 0 20px;
8555}
8556
8557.pca-action-item {
8558    display: flex;
8559    align-items: center;
8560    gap: 16px;
8561    padding: 16px 20px;
8562    background: transparent;
8563    border: 0;
8564    width: 100%;
8565    cursor: pointer;
8566    transition: background-color 0.15s ease;
8567    -webkit-tap-highlight-color: transparent;
8568}
8569
8570.pca-action-item:hover {
8571    background: #f8fafc;
8572}
8573
8574.pca-action-item:active {
8575    background: #f1f5f9;
8576}
8577
8578.pca-action-item:disabled {
8579    opacity: 0.4;
8580    cursor: not-allowed;
8581}
8582
8583.pca-action-item:disabled:hover {
8584    background: transparent;
8585}
8586
8587.pca-action-icon {
8588    width: 24px;
8589    height: 24px;
8590    color: #475569;
8591    flex-shrink: 0;
8592}
8593
8594.pca-action-content {
8595    flex: 1;
8596    text-align: left;
8597}
8598
8599.pca-action-label {
8600    font-size: 15px;
8601    font-weight: 600;
8602    color: #0f172a;
8603    margin: 0 0 2px 0;
8604}
8605
8606.pca-action-desc {
8607    font-size: 12px;
8608    color: #64748b;
8609    margin: 0;
8610}
8611
8612.pca-action-badge {
8613    display: inline-flex;
8614    align-items: center;
8615    justify-content: center;
8616    min-width: 20px;
8617    height: 20px;
8618    padding: 0 6px;
8619    background: #0ea5e9;
8620    color: #fff;
8621    font-size: 11px;
8622    font-weight: 700;
8623    border-radius: 999px;
8624    margin-left: auto;
8625}
8626
8627.pca-action-badge.compare {
8628    background: #8b5cf6;
8629}
8630
8631/* Action Item Colors */
8632.pca-action-item.cart .pca-action-icon {
8633    color: #0ea5e9;
8634}
8635
8636.pca-action-item.compare .pca-action-icon {
8637    color: #8b5cf6;
8638}
8639
8640.pca-action-item.compare.active {
8641    background: rgba(139, 92, 246, 0.05);
8642}
8643
8644.pca-action-item.compare.active .pca-action-label {
8645    color: #8b5cf6;
8646}
8647
8648.pca-action-item.compare.active .pca-action-icon {
8649    color: #8b5cf6;
8650}
8651
8652.pca-action-item.share .pca-action-icon {
8653    color: #64748b;
8654}
8655
8656/* Loading State */
8657.pca-action-item.loading {
8658    pointer-events: none;
8659}
8660
8661.pca-action-item.loading .pca-action-icon {
8662    animation: spin 1s linear infinite;
8663}
8664
8665@keyframes spin {
8666    from { transform: rotate(0deg); }
8667    to { transform: rotate(360deg); }
8668}
8669
8670/* Success State Animation */
8671.pca-action-item.success .pca-action-icon {
8672    animation: checkmark 0.5s ease;
8673}
8674
8675@keyframes checkmark {
8676    0% { transform: scale(0); }
8677    50% { transform: scale(1.2); }
8678    100% { transform: scale(1); }
8679}
8680
8681/* ===== Safe Area for Mobile Devices ===== */
8682@supports (padding-bottom: env(safe-area-inset-bottom)) {
8683    .pca-sheet-actions {
8684        padding-bottom: calc(20px + env(safe-area-inset-bottom));
8685    }
8686}
8687
8688/* ===== Responsive Tweaks ===== */
8689@media (max-width: 640px) {
8690    .pca-bottom-sheet {
8691        border-radius: 16px 16px 0 0;
8692    }
8693    
8694    .pca-sheet-product {
8695        padding: 10px 16px;
8696    }
8697    
8698    .pca-product-image {
8699        width: 50px;
8700        height: 50px;
8701    }
8702    
8703    .pca-product-name {
8704        font-size: 13px;
8705    }
8706    
8707    .pca-product-price {
8708        font-size: 14px;
8709    }
8710    
8711    .pca-action-item {
8712        padding: 14px 16px;
8713        gap: 12px;
8714    }
8715    
8716    .pca-action-icon {
8717        width: 22px;
8718        height: 22px;
8719    }
8720    
8721    .pca-action-label {
8722        font-size: 14px;
8723    }
8724}
8725
8726/* ===== Animation for Flying Product ===== */
8727.flying-product {
8728    position: fixed;
8729    pointer-events: none;
8730    z-index: 99999;
8731    animation: fadeOut 0.8s ease forwards;
8732}
8733
8734@keyframes fadeOut {
8735    to {
8736        opacity: 0;
8737        transform: scale(0.5);
8738    }
8739}
8740
8741/* ===== Desktop Dropdown (Alternative) ===== */
8742@media (min-width: 1024px) {
8743    /* Optional: Could add desktop dropdown menu here */
8744    /* For now, using bottom sheet for consistency */
8745}
8746
8747/* ===== Print Styles ===== */
8748@media print {
8749    .prod-card-actions-btn,
8750    .pca-overlay,
8751    .pca-bottom-sheet {
8752        display: none !important;
8753    }
8754}
8755
8756</style>
8757  <style data-inlined="modal.css">
8758/* Modern Modal Dialog for Cart Operations */
8759.modal-overlay {
8760  position: fixed;
8761  top: 0;
8762  left: 0;
8763  right: 0;
8764  bottom: 0;
8765  background: rgba(0, 0, 0, 0.5);
8766  backdrop-filter: blur(4px);
8767  display: flex;
8768  align-items: center;
8769  justify-content: center;
8770  z-index: 9999;
8771  opacity: 0;
8772  visibility: hidden;
8773  transition: all 0.2s ease;
8774}
8775
8776.modal-overlay.show {
8777  opacity: 1;
8778  visibility: visible;
8779}
8780
8781.modal-dialog {
8782  background: white;
8783  border-radius: 16px;
8784  box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
8785  max-width: 400px;
8786  width: 90%;
8787  max-height: 90vh;
8788  overflow-y: auto;
8789  transform: scale(0.95);
8790  transition: transform 0.2s ease;
8791}
8792
8793.modal-overlay.show .modal-dialog {
8794  transform: scale(1);
8795}
8796
8797.modal-header {
8798  padding: 24px 24px 16px;
8799  border-bottom: 1px solid #f1f5f9;
8800}
8801
8802.modal-title {
8803  font-size: 20px;
8804  font-weight: 700;
8805  color: #0f172a;
8806  margin: 0;
8807  display: flex;
8808  align-items: center;
8809  gap: 12px;
8810}
8811
8812.modal-body {
8813  padding: 16px 24px 24px;
8814}
8815
8816.modal-message {
8817  font-size: 16px;
8818  line-height: 1.5;
8819  color: #475569;
8820  margin: 0;
8821}
8822
8823.modal-actions {
8824  display: flex;
8825  gap: 12px;
8826  margin-top: 24px;
8827}
8828
8829.modal-btn {
8830  flex: 1;
8831  padding: 12px 16px;
8832  border: none;
8833  border-radius: 8px;
8834  font-size: 14px;
8835  font-weight: 600;
8836  cursor: pointer;
8837  transition: all 0.2s ease;
8838  text-align: center;
8839}
8840
8841.modal-btn-secondary {
8842  background: #f8fafc;
8843  color: #64748b;
8844  border: 1px solid #e2e8f0;
8845}
8846
8847.modal-btn-secondary:hover {
8848  background: #f1f5f9;
8849  border-color: #cbd5e1;
8850}
8851
8852.modal-btn-danger {
8853  background: #ef4444;
8854  color: white;
8855}
8856
8857.modal-btn-danger:hover {
8858  background: #dc2626;
8859}
8860
8861.modal-btn-primary {
8862  background: #3b82f6;
8863  color: white;
8864}
8865
8866.modal-btn-primary:hover {
8867  background: #2563eb;
8868}
8869
8870.modal-icon {
8871  font-size: 24px;
8872  width: 32px;
8873  height: 32px;
8874  display: flex;
8875  align-items: center;
8876  justify-content: center;
8877  border-radius: 8px;
8878}
8879
8880.modal-icon.danger {
8881  background: #fef2f2;
8882  color: #ef4444;
8883}
8884
8885.modal-icon.warning {
8886  background: #fffbeb;
8887  color: #f59e0b;
8888}
8889
8890.modal-icon.success {
8891  background: #f0fdf4;
8892  color: #22c55e;
8893}
8894
8895.modal-icon.info {
8896  background: #eff6ff;
8897  color: #3b82f6;
8898}
8899
8900/* Mobile responsive */
8901@media (max-width: 640px) {
8902  .modal-dialog {
8903    margin: 16px;
8904    max-width: none;
8905    width: auto;
8906  }
8907  
8908  .modal-header,
8909  .modal-body {
8910    padding-left: 20px;
8911    padding-right: 20px;
8912  }
8913  
8914  .modal-actions {
8915    flex-direction: column;
8916  }
8917  
8918  .modal-btn {
8919    flex: none;
8920  }
8921}
8922</style>
8923      
8924        <style data-inlined="reviews-points.css">
8925/**
8926 * Reviews & Points System - Styles
8927 * Product reviews, rating display, and point redemption UI
8928 */
8929
8930/* ===================================
8931   PRODUCT REVIEWS
8932   =================================== */
8933
8934.product-tabs {
8935  display: flex;
8936  border-bottom: 2px solid #e5e7eb;
8937  margin: 24px 0 16px;
8938  gap: 8px;
8939}
8940
8941.tab-btn {
8942  padding: 12px 20px;
8943  border: none;
8944  background: none;
8945  cursor: pointer;
8946  font-size: 15px;
8947  font-weight: 500;
8948  color: #6b7280;
8949  border-bottom: 3px solid transparent;
8950  transition: all 0.2s ease;
8951  position: relative;
8952  top: 2px;
8953}
8954
8955.tab-btn:hover {
8956  color: #111827;
8957}
8958
8959.tab-btn.active {
8960  color: #2563eb;
8961  border-bottom-color: #2563eb;
8962}
8963
8964.tab-content {
8965  padding: 20px 0;
8966}
8967
8968/* Review Statistics */
8969.review-stats {
8970  display: grid;
8971  grid-template-columns: 200px 1fr;
8972  gap: 32px;
8973  padding: 24px;
8974  background: #f9fafb;
8975  border-radius: 12px;
8976  margin-bottom: 24px;
8977}
8978
8979.rating-summary {
8980  text-align: center;
8981}
8982
8983.avg-rating {
8984  display: block;
8985  font-size: 48px;
8986  font-weight: 700;
8987  color: #111827;
8988  line-height: 1;
8989}
8990
8991.rating-summary .stars {
8992  font-size: 24px;
8993  color: #fbbf24;
8994  margin: 8px 0;
8995}
8996
8997.total-reviews {
8998  display: block;
8999  font-size: 14px;
9000  color: #6b7280;
9001  margin-top: 4px;
9002}
9003
9004.rating-breakdown {
9005  display: flex;
9006  flex-direction: column;
9007  gap: 8px;
9008}
9009
9010.rating-bar {
9011  display: flex;
9012  align-items: center;
9013  gap: 12px;
9014}
9015
9016.rating-label {
9017  font-size: 14px;
9018  color: #374151;
9019  width: 32px;
9020  text-align: right;
9021}
9022
9023.bar-container {
9024  flex: 1;
9025  height: 8px;
9026  background: #e5e7eb;
9027  border-radius: 4px;
9028  overflow: hidden;
9029}
9030
9031.bar-fill {
9032  height: 100%;
9033  background: linear-gradient(90deg, #fbbf24 0%, #f59e0b 100%);
9034  transition: width 0.3s ease;
9035}
9036
9037.rating-count {
9038  font-size: 14px;
9039  color: #6b7280;
9040  width: 40px;
9041  text-align: right;
9042}
9043
9044/* Review Filters */
9045.review-filters {
9046  display: flex;
9047  justify-content: space-between;
9048  align-items: center;
9049  margin-bottom: 20px;
9050}
9051
9052.review-sort {
9053  padding: 8px 12px;
9054  border: 1px solid #d1d5db;
9055  border-radius: 8px;
9056  font-size: 14px;
9057  background: white;
9058}
9059
9060.write-review-btn {
9061  padding: 10px 20px;
9062  background: #2563eb;
9063  color: white;
9064  border: none;
9065  border-radius: 8px;
9066  font-weight: 500;
9067  cursor: pointer;
9068  transition: background 0.2s;
9069}
9070
9071.write-review-btn:hover {
9072  background: #1d4ed8;
9073}
9074
9075/* Review List */
9076.reviews-list {
9077  display: flex;
9078  flex-direction: column;
9079  gap: 16px;
9080}
9081
9082.review-item {
9083  padding: 20px;
9084  background: white;
9085  border: 1px solid #e5e7eb;
9086  border-radius: 12px;
9087  transition: box-shadow 0.2s;
9088}
9089
9090.review-item:hover {
9091  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
9092}
9093
9094.review-header {
9095  display: flex;
9096  justify-content: space-between;
9097  align-items: flex-start;
9098  margin-bottom: 8px;
9099}
9100
9101.review-author {
9102  display: flex;
9103  align-items: center;
9104  gap: 8px;
9105}
9106
9107.review-author strong {
9108  font-size: 15px;
9109  color: #111827;
9110}
9111
9112.verified-badge {
9113  display: inline-flex;
9114  align-items: center;
9115  gap: 4px;
9116  font-size: 12px;
9117  color: #059669;
9118  background: #d1fae5;
9119  padding: 2px 8px;
9120  border-radius: 4px;
9121  font-weight: 500;
9122}
9123
9124.anonymous-badge {
9125  display: inline-flex;
9126  align-items: center;
9127  font-size: 12px;
9128  color: #6b7280;
9129  background: #f3f4f6;
9130  padding: 2px 6px;
9131  border-radius: 4px;
9132  margin-left: 4px;
9133  cursor: help;
9134}
9135
9136.review-rating .stars {
9137  font-size: 16px;
9138  color: #fbbf24;
9139}
9140
9141.review-date {
9142  font-size: 13px;
9143  color: #9ca3af;
9144  margin-bottom: 12px;
9145}
9146
9147.review-text {
9148  font-size: 15px;
9149  line-height: 1.6;
9150  color: #374151;
9151  margin-bottom: 12px;
9152}
9153
9154.review-images {
9155  display: flex;
9156  gap: 8px;
9157  margin-bottom: 12px;
9158  flex-wrap: wrap;
9159}
9160
9161.review-img {
9162  width: 80px;
9163  height: 80px;
9164  object-fit: cover;
9165  border-radius: 8px;
9166  cursor: pointer;
9167  transition: transform 0.2s;
9168}
9169
9170.review-img:hover {
9171  transform: scale(1.05);
9172}
9173
9174.review-actions {
9175  display: flex;
9176  gap: 12px;
9177  padding-top: 12px;
9178  border-top: 1px solid #f3f4f6;
9179}
9180
9181.btn-helpful {
9182  display: flex;
9183  align-items: center;
9184  gap: 6px;
9185  padding: 6px 12px;
9186  background: #f9fafb;
9187  border: 1px solid #e5e7eb;
9188  border-radius: 6px;
9189  font-size: 13px;
9190  color: #6b7280;
9191  cursor: pointer;
9192  transition: all 0.2s;
9193}
9194
9195.btn-helpful:hover {
9196  background: #f3f4f6;
9197  color: #111827;
9198  border-color: #d1d5db;
9199}
9200
9201.btn-helpful.voted {
9202  background: #dbeafe;
9203  color: #1d4ed8;
9204  border-color: #93c5fd;
9205}
9206
9207.helpful-icon {
9208  font-size: 14px;
9209}
9210
9211.no-reviews {
9212  text-align: center;
9213  padding: 60px 20px;
9214  color: #9ca3af;
9215  font-size: 15px;
9216}
9217
9218.load-more-btn {
9219  display: block;
9220  margin: 24px auto 0;
9221  padding: 12px 32px;
9222  background: white;
9223  border: 1px solid #d1d5db;
9224  border-radius: 8px;
9225  font-size: 14px;
9226  font-weight: 500;
9227  color: #374151;
9228  cursor: pointer;
9229  transition: all 0.2s;
9230}
9231
9232.load-more-btn:hover {
9233  background: #f9fafb;
9234  border-color: #9ca3af;
9235}
9236
9237/* ===================================
9238   POINT BALANCE WIDGET (LEGACY - kept for backward compatibility)
9239   =================================== */
9240
9241.point-balance-card {
9242  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
9243  color: white;
9244  padding: 24px;
9245  border-radius: 16px;
9246  margin-bottom: 24px;
9247  box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
9248}
9249
9250.point-header {
9251  display: flex;
9252  justify-content: space-between;
9253  align-items: center;
9254  margin-bottom: 12px;
9255}
9256
9257.point-header h3 {
9258  font-size: 18px;
9259  font-weight: 600;
9260  margin: 0;
9261  display: flex;
9262  align-items: center;
9263  gap: 8px;
9264}
9265
9266.point-header .balance {
9267  font-size: 32px;
9268  font-weight: 700;
9269}
9270
9271.point-value {
9272  font-size: 14px;
9273  opacity: 0.9;
9274  margin: 8px 0 16px;
9275}
9276
9277.point-stats {
9278  display: flex;
9279  gap: 20px;
9280  font-size: 13px;
9281  opacity: 0.85;
9282  margin-bottom: 16px;
9283}
9284
9285.btn-view-history {
9286  display: inline-block;
9287  padding: 10px 20px;
9288  background: rgba(255, 255, 255, 0.2);
9289  color: white;
9290  text-decoration: none;
9291  border-radius: 8px;
9292  font-size: 14px;
9293  font-weight: 500;
9294  transition: background 0.2s;
9295}
9296
9297.btn-view-history:hover {
9298  background: rgba(255, 255, 255, 0.3);
9299}
9300
9301/* ===================================
9302   CHECKOUT POINT REDEMPTION
9303   =================================== */
9304
9305.checkout-points {
9306  padding: 16px;
9307  background: #fef3c7;
9308  border: 2px solid #fbbf24;
9309  border-radius: 12px;
9310  margin-bottom: 16px;
9311}
9312
9313.points-header {
9314  display: flex;
9315  align-items: center;
9316  gap: 10px;
9317  margin-bottom: 12px;
9318}
9319
9320.points-header input[type="checkbox"] {
9321  width: 18px;
9322  height: 18px;
9323  cursor: pointer;
9324}
9325
9326.points-header label {
9327  font-size: 15px;
9328  color: #78350f;
9329  cursor: pointer;
9330  margin: 0;
9331}
9332
9333#points-slider {
9334  padding: 16px;
9335  background: white;
9336  border-radius: 8px;
9337  margin-top: 12px;
9338}
9339
9340#points-slider label {
9341  display: block;
9342  font-size: 14px;
9343  color: #374151;
9344  margin-bottom: 8px;
9345  font-weight: 500;
9346}
9347
9348#points-amount {
9349  width: 100%;
9350  margin-bottom: 8px;
9351}
9352
9353#points-display {
9354  display: flex;
9355  justify-content: space-between;
9356  font-size: 14px;
9357  color: #111827;
9358  margin-top: 8px;
9359}
9360
9361#points-display .discount-amount {
9362  color: #059669;
9363  font-weight: 600;
9364}
9365
9366.price-row.point-discount {
9367  color: #059669;
9368  font-weight: 500;
9369}
9370
9371/* ===================================
9372   REVIEWABLE ORDERS
9373   =================================== */
9374
9375.reviewable-orders {
9376  background: white;
9377  border: 1px solid #e5e7eb;
9378  border-radius: 12px;
9379  padding: 20px;
9380  margin-bottom: 24px;
9381}
9382
9383.reviewable-orders h3 {
9384  font-size: 18px;
9385  margin-bottom: 16px;
9386  color: #111827;
9387}
9388
9389.order-card {
9390  display: flex;
9391  gap: 16px;
9392  padding: 16px;
9393  background: #f9fafb;
9394  border-radius: 8px;
9395  margin-bottom: 12px;
9396}
9397
9398.order-card:last-child {
9399  margin-bottom: 0;
9400}
9401
9402.order-card img {
9403  width: 80px;
9404  height: 80px;
9405  object-fit: cover;
9406  border-radius: 8px;
9407}
9408
9409.order-info {
9410  flex: 1;
9411}
9412
9413.order-info h4 {
9414  font-size: 15px;
9415  margin: 0 0 4px;
9416  color: #111827;
9417}
9418
9419.order-info p {
9420  font-size: 13px;
9421  color: #6b7280;
9422  margin: 0 0 12px;
9423}
9424
9425.btn-review {
9426  padding: 8px 16px;
9427  background: #059669;
9428  color: white;
9429  border: none;
9430  border-radius: 6px;
9431  font-size: 14px;
9432  font-weight: 500;
9433  cursor: pointer;
9434  transition: background 0.2s;
9435}
9436
9437.btn-review:hover {
9438  background: #047857;
9439}
9440
9441/* ===================================
9442   TOAST NOTIFICATIONS
9443   =================================== */
9444
9445.review-toast {
9446  position: fixed;
9447  bottom: 24px;
9448  right: 24px;
9449  padding: 14px 20px;
9450  background: #111827;
9451  color: white;
9452  border-radius: 8px;
9453  font-size: 14px;
9454  line-height: 1.4;
9455  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
9456  transform: translateY(100px);
9457  opacity: 0;
9458  transition: all 0.3s ease;
9459  z-index: 10000;
9460  max-width: 400px;
9461  word-wrap: break-word;
9462}
9463
9464.review-toast.show {
9465  transform: translateY(0);
9466  opacity: 1;
9467}
9468
9469/* Mobile: Position above bottom-nav */
9470@media (max-width: 768px) {
9471  .review-toast {
9472    bottom: 80px; /* Above bottom-nav (50px height + padding + safe margin) */
9473    right: 16px;
9474    left: 16px;
9475    max-width: calc(100% - 32px);
9476  }
9477}
9478
9479.review-toast.toast-success {
9480  background: #059669;
9481}
9482
9483.review-toast.toast-error {
9484  background: #dc2626;
9485}
9486
9487.review-toast.toast-info {
9488  background: #2563eb;
9489}
9490
9491/* ===================================
9492   RESPONSIVE
9493   =================================== */
9494
9495@media (max-width: 768px) {
9496  .point-balance-card {
9497    border-radius: 0;
9498  }
9499  
9500  .review-stats {
9501    grid-template-columns: 70px 1fr;
9502    gap: 20px;
9503    padding: 16px;
9504  }
9505
9506  .rating-summary {
9507    text-align: center;
9508  }
9509  
9510  .rating-summary .avg-rating {
9511    font-size: 32px;
9512  }
9513  
9514  .rating-summary .stars {
9515    font-size: 18px;
9516    margin: 4px 0;
9517  }
9518  
9519  .total-reviews {
9520    font-size: 11px;
9521  }
9522  
9523  /* Rating breakdown - more compact */
9524  .rating-breakdown {
9525    gap: 2px;
9526  }
9527  
9528  .rating-breakdown .rating-bar {
9529    gap: 4px;
9530  }
9531  
9532  .rating-breakdown .rating-label {
9533    font-size: 11px;
9534    width: 16px;
9535    min-width: 16px;
9536    text-align: left;
9537  }
9538  
9539  .rating-breakdown .bar-container {
9540    height: 6px;
9541  }
9542  
9543  .rating-breakdown .rating-count {
9544    font-size: 11px;
9545    width: 16px;
9546    min-width: 16px;
9547  }
9548  
9549  .review-header {
9550    flex-direction: column;
9551    gap: 8px;
9552    align-items: flex-start;
9553  }
9554  
9555  .review-rating {
9556    order: -1;
9557    margin-bottom: 4px;
9558  }
9559  
9560  .review-rating .stars {
9561    font-size: 14px;
9562  }
9563
9564  .review-filters {
9565    flex-direction: column;
9566    gap: 12px;
9567    align-items: stretch;
9568  }
9569
9570  .review-sort {
9571    width: 100%;
9572  }
9573
9574  .point-header .balance {
9575    font-size: 24px;
9576  }
9577
9578  .point-stats {
9579    flex-direction: column;
9580    gap: 8px;
9581  }
9582
9583  .order-card {
9584    flex-direction: column;
9585  }
9586
9587  .order-card img {
9588    width: 100%;
9589    height: 200px;
9590  }
9591}
9592
9593/* ===================================
9594   LOADING & ERROR STATES
9595   =================================== */
9596
9597.review-loading {
9598  text-align: center;
9599  padding: 40px;
9600  color: #9ca3af;
9601  font-size: 14px;
9602}
9603
9604.review-error {
9605  text-align: center;
9606  padding: 40px;
9607  color: #dc2626;
9608  font-size: 14px;
9609  background: #fee2e2;
9610  border-radius: 8px;
9611}
9612
9613.review-empty {
9614  text-align: center;
9615  padding: 10px;
9616  color: #6b7280;
9617  font-size: 14px;
9618}
9619
9620/* ===================================
9621   REVIEWABLE ORDERS LIST
9622   =================================== */
9623
9624.reviewable-orders-list {
9625  display: flex;
9626  flex-direction: column;
9627  gap: 16px;
9628}
9629
9630.reviewable-order-item {
9631  display: grid;
9632  grid-template-columns: 80px 1fr auto;
9633  gap: 16px;
9634  padding: 16px;
9635  background: #fff;
9636  border: 1px solid #e5e7eb;
9637  border-radius: 12px;
9638  align-items: center;
9639  transition: box-shadow 0.2s;
9640}
9641
9642.reviewable-order-item:hover {
9643  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
9644}
9645
9646.reviewable-order-item .roi-image {
9647  width: 80px;
9648  height: 80px;
9649  object-fit: cover;
9650  border-radius: 8px;
9651  border: 1px solid #e5e7eb;
9652}
9653
9654.reviewable-order-item .roi-info {
9655  display: flex;
9656  flex-direction: column;
9657  gap: 4px;
9658}
9659
9660.reviewable-order-item .roi-info h4 {
9661  margin: 0;
9662  font-size: 15px;
9663  font-weight: 600;
9664  color: #111827;
9665  line-height: 1.4;
9666}
9667
9668.reviewable-order-item .roi-meta {
9669  margin: 0;
9670  font-size: 13px;
9671  color: #6b7280;
9672}
9673
9674.reviewable-order-item .btn-write-review {
9675  white-space: nowrap;
9676  padding: 10px 20px;
9677  font-size: 14px;
9678}
9679
9680@media (max-width: 640px) {
9681  .reviewable-order-item {
9682    grid-template-columns: 60px 1fr;
9683    gap: 12px;
9684  }
9685  
9686  .reviewable-order-item .roi-image {
9687    width: 60px;
9688    height: 60px;
9689  }
9690  
9691  .reviewable-order-item .btn-write-review {
9692    grid-column: 1 / -1;
9693    width: 100%;
9694  }
9695}
9696
9697/* ===================================
9698   REVIEW SUBMISSION MODAL
9699   =================================== */
9700
9701/* Modal Overlay Background */
9702#review-modal-overlay {
9703  position: fixed;
9704  top: 0;
9705  left: 0;
9706  right: 0;
9707  bottom: 0;
9708  background: rgba(0, 0, 0, 0.6) !important;
9709  backdrop-filter: blur(8px);
9710  display: flex;
9711  align-items: center;
9712  justify-content: center;
9713  z-index: 99999 !important;
9714  padding: 20px;
9715}
9716
9717.review-modal {
9718  max-width: 600px !important;
9719  width: 90% !important;
9720  background: white;
9721  border-radius: 16px !important;
9722  box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
9723  max-height: 90vh;
9724  display: flex;
9725  flex-direction: column;
9726  overflow: hidden !important;
9727}
9728
9729#review-modal-overlay .modal-content,
9730.review-modal .modal-content {
9731  padding: 0 !important;
9732  flex: initial !important;
9733  overflow-y: visible !important;
9734}
9735
9736.review-modal .modal-header {
9737  display: flex;
9738  justify-content: space-between;
9739  align-items: center;
9740  padding: 20px 24px;
9741  border-bottom: 1px solid #e5e7eb;
9742}
9743
9744.review-modal .modal-title {
9745  margin: 0;
9746  font-size: 20px;
9747  font-weight: 700;
9748  color: #111827;
9749}
9750
9751.review-modal .modal-close {
9752  background: none;
9753  border: none;
9754  cursor: pointer;
9755  padding: 4px;
9756  color: #6b7280;
9757  transition: color 0.2s;
9758  display: flex;
9759  align-items: center;
9760  justify-content: center;
9761}
9762
9763.review-modal .modal-close:hover {
9764  color: #111827;
9765}
9766
9767.review-modal .modal-body {
9768  padding: 24px;
9769  max-height: calc(90vh - 140px);
9770  overflow-y: auto;
9771}
9772
9773/* Custom Scrollbar for Modal */
9774.review-modal .modal-body::-webkit-scrollbar {
9775  width: 8px;
9776}
9777
9778.review-modal .modal-body::-webkit-scrollbar-track {
9779  background: #f1f5f9;
9780  border-radius: 4px;
9781}
9782
9783.review-modal .modal-body::-webkit-scrollbar-thumb {
9784  background: linear-gradient(180deg, #3b82f6 0%, #2563eb 100%);
9785  border-radius: 4px;
9786}
9787
9788.review-modal .modal-body::-webkit-scrollbar-thumb:hover {
9789  background: linear-gradient(180deg, #2563eb 0%, #1d4ed8 100%);
9790}
9791
9792/* Firefox Scrollbar */
9793.review-modal .modal-body {
9794  scrollbar-width: thin;
9795  scrollbar-color: #3b82f6 #f1f5f9;
9796}
9797
9798.review-modal .modal-footer {
9799  display: flex;
9800  gap: 12px;
9801  margin-top: 24px;
9802  padding-top: 20px;
9803  border-top: 1px solid #f1f5f9;
9804}
9805
9806.review-product-info {
9807  display: flex;
9808  gap: 16px;
9809  padding: 16px;
9810  background: #f9fafb;
9811  border-radius: 8px;
9812  margin-bottom: 24px;
9813}
9814
9815.review-product-thumb {
9816  width: 80px;
9817  height: 80px;
9818  object-fit: cover;
9819  border-radius: 6px;
9820  border: 1px solid #e5e7eb;
9821}
9822
9823.review-product-details h4 {
9824  margin: 0 0 8px 0;
9825  font-size: 16px;
9826  font-weight: 600;
9827  color: #111827;
9828}
9829
9830.review-product-details p {
9831  margin: 0;
9832  font-size: 13px;
9833  color: #6b7280;
9834}
9835
9836/* Star Rating Input */
9837.star-rating-input {
9838  display: inline-flex;
9839  gap: 8px;
9840  margin-right: 12px;
9841  vertical-align: middle;
9842}
9843
9844.star-btn {
9845  background: none;
9846  border: none;
9847  font-size: 32px;
9848  color: #d1d5db;
9849  cursor: pointer;
9850  transition: all 0.2s;
9851  padding: 0;
9852  line-height: 1;
9853}
9854
9855.star-btn:hover,
9856.star-btn.active {
9857  color: #fbbf24;
9858  transform: scale(1.1);
9859}
9860
9861.rating-label {
9862  display: inline-block;
9863  font-size: 15px;
9864  color: #6b7280;
9865  margin: 0;
9866  vertical-align: middle;
9867  min-width: 150px;
9868}
9869
9870/* Form Elements */
9871.review-form .form-group {
9872  margin-bottom: 20px;
9873}
9874
9875.review-form .form-label {
9876  display: block;
9877  font-size: 14px;
9878  font-weight: 600;
9879  color: #374151;
9880  margin-bottom: 8px;
9881}
9882
9883.review-form .form-control {
9884  width: 100%;
9885  padding: 12px;
9886  border: 1px solid #d1d5db;
9887  border-radius: 8px;
9888  font-size: 14px;
9889  font-family: inherit;
9890  transition: border-color 0.2s;
9891}
9892
9893.review-form .form-control:focus {
9894  outline: none;
9895  border-color: #2563eb;
9896  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
9897}
9898
9899.review-form .form-hint {
9900  font-size: 13px;
9901  color: #6b7280;
9902  margin-top: 8px;
9903}
9904
9905/* Image Upload */
9906.btn-upload-trigger {
9907  display: inline-flex;
9908  align-items: center;
9909  gap: 8px;
9910  padding: 10px 16px;
9911  background: #f9fafb;
9912  border: 2px dashed #d1d5db;
9913  border-radius: 8px;
9914  color: #374151;
9915  cursor: pointer;
9916  font-size: 14px;
9917  font-weight: 500;
9918  transition: all 0.2s;
9919}
9920
9921.btn-upload-trigger:hover {
9922  background: #f3f4f6;
9923  border-color: #2563eb;
9924  color: #2563eb;
9925}
9926
9927.image-preview-grid {
9928  display: grid;
9929  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
9930  gap: 12px;
9931  margin-top: 16px;
9932}
9933
9934.image-preview-item {
9935  position: relative;
9936  aspect-ratio: 1;
9937  border-radius: 8px;
9938  overflow: hidden;
9939  border: 1px solid #e5e7eb;
9940}
9941
9942.image-preview-item img {
9943  width: 100%;
9944  height: 100%;
9945  object-fit: cover;
9946}
9947
9948.remove-image {
9949  position: absolute;
9950  top: 4px;
9951  right: 4px;
9952  background: rgba(0, 0, 0, 0.7);
9953  border: none;
9954  color: white;
9955  width: 24px;
9956  height: 24px;
9957  border-radius: 50%;
9958  cursor: pointer;
9959  display: flex;
9960  align-items: center;
9961  justify-content: center;
9962  transition: background 0.2s;
9963}
9964
9965.remove-image:hover {
9966  background: rgba(0, 0, 0, 0.9);
9967}
9968
9969/* Point Reward Info */
9970.point-reward-info {
9971  background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
9972  border: 2px solid #fbbf24;
9973  border-radius: 12px;
9974  padding: 16px;
9975  margin: 20px 0;
9976}
9977
9978.pri-header {
9979  display: flex;
9980  align-items: center;
9981  gap: 8px;
9982  font-size: 16px;
9983  font-weight: 700;
9984  color: #92400e;
9985  margin-bottom: 12px;
9986}
9987
9988.pri-list {
9989  list-style: none;
9990  padding: 0;
9991  margin: 0 0 12px 0;
9992}
9993
9994.pri-list li {
9995  padding: 6px 0;
9996  font-size: 14px;
9997  color: #78350f;
9998  transition: opacity 0.2s;
9999}
10000
10001.pri-total {
10002  padding-top: 12px;
10003  border-top: 2px solid rgba(251, 191, 36, 0.3);
10004  font-size: 16px;
10005  color: #92400e;
10006  font-weight: 600;
10007}
10008
10009.form-error {
10010  background: #fee2e2;
10011  border: 1px solid #f87171;
10012  color: #991b1b;
10013  padding: 12px;
10014  border-radius: 8px;
10015  font-size: 14px;
10016  margin-top: 16px;
10017}
10018
10019/* Buttons */
10020.review-form .btn {
10021  padding: 12px 24px;
10022  border: none;
10023  border-radius: 8px;
10024  font-size: 15px;
10025  font-weight: 600;
10026  cursor: pointer;
10027  transition: all 0.2s;
10028}
10029
10030.review-form .btn-primary {
10031  background: #2563eb;
10032  color: white;
10033}
10034
10035.review-form .btn-primary:hover {
10036  background: #1d4ed8;
10037}
10038
10039.review-form .btn-primary:disabled {
10040  background: #94a3b8;
10041  cursor: not-allowed;
10042}
10043
10044.review-form .btn-secondary {
10045  background: #f1f5f9;
10046  color: #64748b;
10047}
10048
10049.review-form .btn-secondary:hover {
10050  background: #e2e8f0;
10051  color: #475569;
10052}
10053
10054@media (max-width: 768px) {
10055    #review-modal-overlay { padding: 0; }
10056    .review-modal { max-width: 100%; width: 100% !important; }
10057}
10058
10059/* ===================================
10060   POINT SUMMARY GRID (Profile Page)
10061   =================================== */
10062
10063.point-summary-grid {
10064  display: grid;
10065  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
10066  gap: 12px;
10067  margin-bottom: 32px;
10068}
10069
10070.point-summary-card {
10071  background: linear-gradient(135deg, #f9fafb 0%, #ffffff 100%);
10072  border: 1px solid #e5e7eb;
10073  border-radius: 12px;
10074  padding: 16px 20px;
10075  display: flex;
10076  align-items: center;
10077  justify-content: space-between;
10078  gap: 12px;
10079  transition: all 0.2s;
10080}
10081
10082.point-summary-card:hover {
10083  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
10084  transform: translateY(-2px);
10085}
10086
10087.point-summary-card .psc-left {
10088  display: flex;
10089  flex-direction: column;
10090  gap: 2px;
10091}
10092
10093.point-summary-card .psc-icon {
10094  font-size: 24px;
10095  margin-bottom: 4px;
10096}
10097
10098.point-summary-card .psc-label {
10099  font-size: 13px;
10100  color: #6b7280;
10101  font-weight: 500;
10102  text-transform: uppercase;
10103  letter-spacing: 0.5px;
10104}
10105
10106.point-summary-card .psc-value {
10107  font-size: 26px;
10108  font-weight: 700;
10109  color: #111827;
10110  flex-shrink: 0;
10111}
10112
10113/* Point History Section */
10114.point-history-section {
10115  margin-top: 32px;
10116}
10117
10118.point-history-section h3 {
10119  font-size: 18px;
10120  font-weight: 600;
10121  color: #111827;
10122  margin-bottom: 16px;
10123}
10124
10125.point-history-list {
10126  display: flex;
10127  flex-direction: column;
10128  gap: 12px;
10129}
10130
10131.point-history-item {
10132  display: grid;
10133  grid-template-columns: 40px 1fr auto;
10134  gap: 16px;
10135  padding: 16px;
10136  background: white;
10137  border: 1px solid #e5e7eb;
10138  border-radius: 10px;
10139  align-items: center;
10140  transition: all 0.2s;
10141}
10142
10143.point-history-item:hover {
10144  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
10145}
10146
10147.point-history-item.credit {
10148  border-left: 3px solid #10b981;
10149}
10150
10151.point-history-item.debit {
10152  border-left: 3px solid #f59e0b;
10153}
10154
10155.point-history-item .phi-icon {
10156  width: 40px;
10157  height: 40px;
10158  display: flex;
10159  align-items: center;
10160  justify-content: center;
10161  font-size: 20px;
10162  border-radius: 50%;
10163  background: #f9fafb;
10164}
10165
10166.point-history-item.credit .phi-icon {
10167  background: #d1fae5;
10168}
10169
10170.point-history-item.debit .phi-icon {
10171  background: #fef3c7;
10172}
10173
10174.point-history-item .phi-details {
10175  display: flex;
10176  flex-direction: column;
10177  gap: 4px;
10178}
10179
10180.point-history-item .phi-desc {
10181  font-size: 15px;
10182  font-weight: 500;
10183  color: #111827;
10184}
10185
10186.point-history-item .phi-date {
10187  font-size: 13px;
10188  color: #9ca3af;
10189}
10190
10191.point-history-item .phi-amount {
10192  font-size: 18px;
10193  font-weight: 700;
10194}
10195
10196.point-history-item .phi-amount.credit {
10197  color: #10b981;
10198}
10199
10200.point-history-item .phi-amount.debit {
10201  color: #f59e0b;
10202}
10203
10204@media (max-width: 640px) {
10205  .point-summary-grid {
10206    grid-template-columns: 1fr;
10207  }
10208  
10209  .point-summary-card .psc-value {
10210    font-size: 22px;
10211  }
10212
10213  .point-history-item {
10214    grid-template-columns: 36px 1fr;
10215    gap: 12px;
10216  }
10217  
10218  .point-history-item .phi-amount {
10219    grid-column: 2;
10220    text-align: right;
10221    margin-top: 8px;
10222  }
10223}
10224
10225/* ── Pending points: compact widget indicator ── */
10226.pbc-pending {
10227  font-size: 12px;
10228  opacity: 0.85;
10229  margin-top: 4px;
10230  color: #fde68a;
10231}
10232
10233/* ── Pending points: summary card ── */
10234.point-summary-card.psc-pending-card {
10235  background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
10236  border-color: #fcd34d;
10237}
10238
10239.point-summary-card.psc-pending-card .psc-value {
10240  color: #b45309;
10241}
10242
10243.point-summary-card .psc-sub {
10244  font-size: 11px;
10245  color: #9ca3af;
10246  margin-top: 4px;
10247}
10248
10249/* ── Pending points: history item ── */
10250.point-history-item.phi-pending {
10251  opacity: 0.75;
10252  border-left-color: #fcd34d;
10253  background: #fffbeb;
10254}
10255
10256.point-history-item.phi-pending .phi-icon {
10257  background: #fef3c7;
10258}
10259
10260.phi-pending-badge {
10261  display: inline-block;
10262  font-size: 11px;
10263  font-weight: 600;
10264  padding: 2px 7px;
10265  border-radius: 10px;
10266  background: #fef3c7;
10267  color: #92400e;
10268  border: 1px solid #fcd34d;
10269  margin-left: 8px;
10270  vertical-align: middle;
10271}
10272
10273/* ── Reversed points: history item ── */
10274.point-history-item.phi-reversed {
10275  opacity: 0.55;
10276  border-left-color: #d1d5db;
10277  background: #f9fafb;
10278}
10279
10280.point-history-item.phi-reversed .phi-icon {
10281  background: #f3f4f6;
10282}
10283
10284.point-history-item.phi-reversed .phi-amount {
10285  text-decoration: line-through;
10286  color: #9ca3af !important;
10287}
10288
10289.phi-reversed-badge {
10290  display: inline-block;
10291  font-size: 11px;
10292  font-weight: 600;
10293  padding: 2px 7px;
10294  border-radius: 10px;
10295  background: #fee2e2;
10296  color: #991b1b;
10297  border: 1px solid #fca5a5;
10298  margin-left: 8px;
10299  vertical-align: middle;
10300}
10301
10302</style>
10303            
10304</head>
10305<body>
10306<header class="topbar">
10307  <div class="container topbar-inner">
10308    <div class="topbar-left">
10309      LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL 
10310    </div>
10311    <div class="topbar-right">
10312      <a href="https://loklokpro.com/">IWANTOGEL</a>
10313      <a href="https://loklokpro.com/">TOTO SLOT</a>
10314      <a href="https://loklokpro.com/">TOTO TOGEL</a>
10315      <a href="https://loklokpro.com/">SITUS PALING MUDAH MENANG</a>
10316    </div>
10317  </div>
10318</header>
10319<nav class="navbar">
10320  <nav class="navbar-logo" style="width: 100%;">
10321    <div class="container nav-inner" style="justify-items: center; justify-content: center;">
10322        <a href="https://loklokpro.com/" class="logo-mobile" title="IWANTOGEL">
10323          <img src="https://area-terpercaya.b-cdn.net/newlogo.gif" alt="IWANTOGEL" height="40">
10324        </a>
10325    </div>
10326</nav>
10327  <div class="container nav-inner">
10328    <a href="https://loklokpro.com/" class="logo" title="IWANTOGEL">
10329      <img src="https://area-terpercaya.b-cdn.net/newlogo.gif" alt="IWANTOGEL" height="40">
10330    </a>
10331
10332    
10333    <form class="search" method="get" action="#">
10334      <input type="hidden" name="page" value="produk">
10335      
10336      <!-- Search Icon (Desktop) -->
10337      <button type="submit" class="search-icon-btn" id="searchBtn" aria-label="Cari">
10338        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
10339          <circle cx="11" cy="11" r="8"/>
10340          <path d="m21 21-4.35-4.35"/>
10341        </svg>
10342      </button>
10343      
10344      <input type="text" name="q" id="searchInput" placeholder="Cari kami Di google : Iwantogel.com"
10345             value="" autocomplete="off"> 
10346      
10347      <div class="search-suggest" id="searchSuggest" hidden></div>
10348    </form>
10349
10350    <div class="nav-actions">
10351      <!--<button class="icon-btn" id="notifBtn" title="Notifikasi">🔔</button>-->
10352      <button class="icon-btn cart-indicator" id="cartBtn" title="Keranjang">
10353        🛒 <span id="cartCount" class="badge" data-role="cart-badge" aria-live="polite">0</span>
10354      </button>
10355
10356      <button class="icon-btn compare-indicator" id="compareBtn" title="Perbandingan">
10357        ⚖️ <span id="compareCount" class="badge" data-role="compare-badge" aria-live="polite">0</span>
10358      </button>
10359      
10360      <!-- Dynamic login/logout button -->
10361            <button class="login-btn" id="loginBtn" aria-label="Masuk">
10362        <span class="login-text">Masuk</span>
10363        <span class="login-ico" aria-hidden="true">😊</span>
10364      </button>
10365          </div>
10366  </div>
10367
10368  <div class="container" style="display: flex; align-items: center;">
10369      
10370
10371      <div class="mega-cats-wrapper">
10372        <div class="mega-cats container" id="megaCatsScroll" style="padding-left: 5px !important;">
10373                      <a class="" href="https://loklokpro.com/">
10374              IWANTOGEL           </a>
10375                      <a class="" href="https://loklokpro.com/">
10376              TOTO SLOT ONLINE            </a>
10377                      <a class="" href="https://loklokpro.com/">
10378              TOTO TOGEL ONLINE            </a>
10379                      <a class="" href="https://loklokpro.com/">
10380              SLOT GACOR MUDAH MENANG           </a>
10381                      <a class="" href="https://loklokpro.com/">
10382              BONUS MELIMPAH            </a>
10383                      <a class="" href="https://loklokpro.com/">
10384              CLAIM SEKARANG JUGA            </a>
10385                  </div>
10386        <div class="mega-cats-fade" id="megaCatsFade"></div>
10387      </div>
10388  </div>
10389</nav>
10390
10391<!-- Panel Mega Menu - Outside navbar for full-screen -->
10392<div class="mm2-panel" data-mm2="panel" role="dialog" aria-label="Kategori">
10393  <div class="mm2-view is-active" data-mm2="top"></div>
10394  <div class="mm2-view" data-mm2="detail"></div>
10395</div>
10396
10397<!-- Backdrop - Outside navbar for full-screen -->
10398<div class="mm2-backdrop" data-mm2="backdrop"></div>
10399
10400<!-- Mobile Bottom Navigation -->
10401<nav class="bottom-nav">
10402  <div class="bottom-nav-inner">
10403    <!-- Home -->
10404    <a href="/" class="bottom-nav-item" id="bottomHomeBtn" data-page="home">
10405      <div class="bottom-nav-item-icon">
10406        <!-- Outlined icon (default) -->
10407        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10408          <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
10409          <polyline points="9 22 9 12 15 12 15 22"/>
10410        </svg>
10411        <!-- Filled icon (active) -->
10412        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10413          <path d="M11.47 3.84a.75.75 0 011.06 0l8.69 8.69a.75.75 0 010 1.06l-.03.03a.75.75 0 01-1.06 0l-8.16-8.16-8.16 8.16a.75.75 0 01-1.06 0l-.03-.03a.75.75 0 010-1.06l8.69-8.69z"/>
10414          <path d="M12 5.5l-7.5 7.5v8.25c0 .414.336.75.75.75h3.75v-6h6v6h3.75a.75.75 0 00.75-.75V13l-7.5-7.5z"/>
10415        </svg>
10416      </div>
10417      <span class="bottom-nav-item-label">Home</span>
10418    </a>
10419
10420    <!-- Kategori -->
10421    <button class="bottom-nav-item" id="bottomCategoryBtn" data-mm2="trigger" aria-label="Kategori">
10422      <div class="bottom-nav-item-icon">
10423        <!-- Outlined icon (default) -->
10424        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10425          <line x1="3" y1="12" x2="21" y2="12"/>
10426          <line x1="3" y1="6" x2="21" y2="6"/>
10427          <line x1="3" y1="18" x2="21" y2="18"/>
10428        </svg>
10429        <!-- Filled icon (active) -->
10430        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10431          <path d="M3 5.25A.75.75 0 013.75 4.5h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 5.25zM3 12a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75A.75.75 0 013 12zm0 6.75a.75.75 0 01.75-.75h16.5a.75.75 0 010 1.5H3.75a.75.75 0 01-.75-.75z"/>
10432        </svg>
10433      </div>
10434      <span class="bottom-nav-item-label">Kategori</span>
10435    </button>
10436
10437    <!-- Keranjang -->
10438    <button class="bottom-nav-item" id="bottomCartBtn">
10439      <div class="bottom-nav-item-icon">
10440        <!-- Outlined icon (default) -->
10441        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10442          <circle cx="9" cy="21" r="1"/>
10443          <circle cx="20" cy="21" r="1"/>
10444          <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>
10445        </svg>
10446        <!-- Filled icon (active) -->
10447        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10448          <path d="M2.25 2.25a.75.75 0 000 1.5h1.386c.17 0 .318.114.362.278l2.558 9.592a3.752 3.752 0 00-2.806 3.63c0 .414.336.75.75.75h15.75a.75.75 0 000-1.5H5.378A2.25 2.25 0 017.5 15h11.218a.75.75 0 00.674-.421 60.358 60.358 0 002.96-7.228.75.75 0 00-.525-.965A60.864 60.864 0 005.68 4.509l-.232-.867A1.875 1.875 0 003.636 2.25H2.25zM3.75 20.25a1.5 1.5 0 113 0 1.5 1.5 0 01-3 0zM16.5 20.25a1.5 1.5 0 113 0 1.5 1.5 0 01-3 0z"/>
10449        </svg>
10450        <span class="bottom-nav-item-badge" id="bottomCartCount">0</span>
10451      </div>
10452      <span class="bottom-nav-item-label">Keranjang</span>
10453    </button>
10454
10455    <!-- Compare -->
10456    <button class="bottom-nav-item" id="bottomCompareBtn">
10457      <div class="bottom-nav-item-icon">
10458        <!-- Outlined icon (default) -->
10459        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10460          <polyline points="17 11 21 7 17 3"/>
10461          <path d="M21 7H9"/>
10462          <polyline points="7 21 3 17 7 13"/>
10463          <path d="M15 17H3"/>
10464        </svg>
10465        <!-- Filled icon (active) -->
10466        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10467          <path d="M21.75 6.75a.75.75 0 00-.75-.75H9a.75.75 0 000 1.5h10.19L15.22 11.47a.75.75 0 001.06 1.06l4.5-4.5a.75.75 0 00.22-.53.75.75 0 00-.22-.53l-.03-.03zM8.78 12.47L4.28 16.97a.75.75 0 000 1.06l4.5 4.5a.75.75 0 001.06-1.06L6.06 18H15a.75.75 0 000-1.5H4.81l3.97-3.97a.75.75 0 00-1.06-1.06z"/>
10468        </svg>
10469        <span class="bottom-nav-item-badge" id="bottomCompareCount" style="display:none;">0</span>
10470      </div>
10471      <span class="bottom-nav-item-label">Compare</span>
10472    </button>
10473
10474    <!-- Chat - Always show, check login in JavaScript -->
10475    <button class="bottom-nav-item" id="bottomChatBtn" data-page="chat">
10476      <div class="bottom-nav-item-icon">
10477        <!-- Outlined icon (default) -->
10478        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" opacity="0.85">
10479          <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
10480        </svg>
10481        <!-- Filled icon (active) -->
10482        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10483          <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/>
10484        </svg>
10485        <span class="bottom-nav-item-badge" id="bottomChatCount" style="display:none;">0</span>
10486      </div>
10487      <span class="bottom-nav-item-label">Chat</span>
10488    </button>
10489
10490    <!-- Akun -->
10491        <button class="bottom-nav-item" id="bottomLoginBtn">
10492      <div class="bottom-nav-item-icon">
10493        <!-- Outlined icon (default) -->
10494        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10495          <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
10496          <circle cx="12" cy="7" r="4"/>
10497        </svg>
10498        <!-- Filled icon (active) -->
10499        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10500          <path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z" clip-rule="evenodd"/>
10501        </svg>
10502      </div>
10503      <span class="bottom-nav-item-label">Login</span>
10504    </button>
10505      </div>
10506</nav>
10507
10508<!-- Mobile User Menu Bottom Sheet -->
10509
10510<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
10511(function(){
10512  const btn = document.getElementById('megaMoreBtn');
10513  const dd  = document.getElementById('megaMoreDropdown');
10514  if(!btn || !dd) return;
10515
10516  function openDD(){
10517    dd.hidden = false;
10518    btn.setAttribute('aria-expanded','true');
10519    document.addEventListener('click', outside, {capture:true});
10520    document.addEventListener('keydown', esc);
10521  }
10522  function closeDD(){
10523    if(dd.hidden) return;
10524    dd.hidden = true;
10525    btn.setAttribute('aria-expanded','false');
10526    document.removeEventListener('click', outside, {capture:true});
10527    document.removeEventListener('keydown', esc);
10528  }
10529  function toggle(){ dd.hidden ? openDD() : closeDD(); }
10530  function outside(e){
10531    if(!dd.contains(e.target) && e.target !== btn){
10532      closeDD();
10533    }
10534  }
10535  function esc(e){
10536    if(e.key === 'Escape'){
10537      closeDD();
10538      btn.focus();
10539    }
10540  }
10541  btn.addEventListener('click', toggle);
10542})();
10543
10544// ========== HORIZONTAL SCROLL FADE INDICATOR ==========
10545(function(){
10546  const megaCats = document.getElementById('megaCatsScroll');
10547  const fade = document.getElementById('megaCatsFade');
10548  if (!megaCats || !fade) return;
10549
10550  function updateFade() {
10551    const scrollLeft = megaCats.scrollLeft;
10552    const scrollWidth = megaCats.scrollWidth;
10553    const clientWidth = megaCats.clientWidth;
10554    const maxScroll = scrollWidth - clientWidth;
10555    
10556    // Hide fade when scrolled to end (within 10px tolerance)
10557    if (maxScroll - scrollLeft <= 10) {
10558      fade.style.opacity = '0';
10559    } else {
10560      fade.style.opacity = '1';
10561    }
10562  }
10563
10564  // Update on scroll
10565  megaCats.addEventListener('scroll', updateFade, { passive: true });
10566  
10567  // Update on resize
10568  window.addEventListener('resize', updateFade, { passive: true });
10569  
10570  // Initial check
10571  setTimeout(updateFade, 100);
10572})();
10573
10574document.addEventListener('DOMContentLoaded', function () {
10575  var input = document.getElementById('searchInput');
10576  if (!input) return;
10577  input.addEventListener('keydown', function (e) {
10578    if (e.key === 'Enter') {
10579      e.preventDefault();
10580      var form = input.form;
10581      if (!form) return;
10582      if (typeof form.requestSubmit === 'function') form.requestSubmit();
10583      else form.submit();
10584    }
10585  });
10586});
10587
10588
10589(function(){
10590  const cartBtn = document.getElementById('cartBtn');
10591  if (cartBtn) {
10592    cartBtn.addEventListener('click', function(e) {
10593      e.preventDefault();
10594      
10595      const checkLogin = () => {
10596        return window.isUserLoggedIn ? window.isUserLoggedIn() : false;
10597      };
10598      
10599      if (checkLogin()) {
10600        window.location.href = '/keranjang';
10601      } else {
10602        showCartLoginModal();
10603      }
10604    });
10605  }
10606
10607  const loginBtn = document.getElementById('loginBtn');
10608  if (loginBtn) {
10609    loginBtn.addEventListener('click', function(e) {
10610      e.preventDefault();
10611      window.location.href = 'https://loklokpro.com/';
10612    });
10613  }
10614
10615  const userMenuBtn = document.getElementById('userMenuBtn');
10616  const userDropdown = document.getElementById('userDropdown');
10617  
10618  if (userMenuBtn && userDropdown) {
10619    function openDropdown() {
10620      userDropdown.hidden = false;
10621      userMenuBtn.setAttribute('aria-expanded', 'true');
10622      document.addEventListener('click', closeOnOutside, { capture: true });
10623      document.addEventListener('keydown', closeOnEscape);
10624    }
10625    
10626    function closeDropdown() {
10627      if (userDropdown.hidden) return;
10628      userDropdown.hidden = true;
10629      userMenuBtn.setAttribute('aria-expanded', 'false');
10630      document.removeEventListener('click', closeOnOutside, { capture: true });
10631      document.removeEventListener('keydown', closeOnEscape);
10632    }
10633    
10634    function closeOnOutside(e) {
10635      if (!userDropdown.contains(e.target) && e.target !== userMenuBtn) {
10636        closeDropdown();
10637      }
10638    }
10639    
10640    function closeOnEscape(e) {
10641      if (e.key === 'Escape') {
10642        closeDropdown();
10643        userMenuBtn.focus();
10644      }
10645    }
10646    
10647    userMenuBtn.addEventListener('click', function(e) {
10648      e.preventDefault();
10649      userDropdown.hidden ? openDropdown() : closeDropdown();
10650    });
10651    
10652    // Close dropdown when clicking profile links
10653    userDropdown.querySelectorAll('[data-close-dropdown]').forEach(link => {
10654      link.addEventListener('click', function() {
10655        closeDropdown();
10656      });
10657    });
10658  }
10659
10660  // Bottom nav profile button - toggle mobile bottom sheet
10661  const bottomProfileBtn = document.getElementById('bottomProfileBtn');
10662  const mobileUserSheet = document.getElementById('mobileUserSheet');
10663  const mobileUserSheetBackdrop = document.getElementById('mobileUserSheetBackdrop');
10664  
10665  if (bottomProfileBtn && mobileUserSheet) {
10666    function openMobileSheet() {
10667      mobileUserSheet.hidden = false;
10668      document.body.style.overflow = 'hidden';
10669      // Trigger animation
10670      requestAnimationFrame(() => {
10671        mobileUserSheet.classList.add('active');
10672      });
10673    }
10674    
10675    function closeMobileSheet() {
10676      mobileUserSheet.classList.remove('active');
10677      document.body.style.overflow = '';
10678      setTimeout(() => {
10679        mobileUserSheet.hidden = true;
10680      }, 300);
10681    }
10682    
10683    bottomProfileBtn.addEventListener('click', function(e) {
10684      e.preventDefault();
10685      openMobileSheet();
10686    });
10687    
10688    if (mobileUserSheetBackdrop) {
10689      mobileUserSheetBackdrop.addEventListener('click', closeMobileSheet);
10690    }
10691    
10692    // Close on escape key
10693    document.addEventListener('keydown', function(e) {
10694      if (e.key === 'Escape' && !mobileUserSheet.hidden) {
10695        closeMobileSheet();
10696      }
10697    });
10698    
10699    // Close sheet when clicking profile links
10700    mobileUserSheet.querySelectorAll('[data-close-sheet]').forEach(link => {
10701      link.addEventListener('click', function() {
10702        closeMobileSheet();
10703      });
10704    });
10705  }
10706  
10707  // Handle Google avatar error in bottom-nav (fallback to initials)
10708  const bottomNavAvatar = document.querySelector('.bottom-nav-avatar-img');
10709  if (bottomNavAvatar) {
10710    bottomNavAvatar.addEventListener('error', function() {
10711      const initials = this.getAttribute('data-fallback-initials') || 'U';
10712      const initialsDiv = document.createElement('div');
10713      initialsDiv.className = 'bottom-nav-avatar-initials';
10714      initialsDiv.textContent = initials;
10715      this.parentNode.replaceChild(initialsDiv, this);
10716    });
10717  }
10718
10719  // Mobile logout button
10720  const mobileLogoutBtn = document.getElementById('mobileLogoutBtn');
10721  if (mobileLogoutBtn) {
10722    mobileLogoutBtn.addEventListener('click', async function(e) {
10723      e.preventDefault();
10724      
10725      // Check push notification before logout
10726      if (window.ChatNotification && typeof window.ChatNotification.confirmLogoutWithPushNotification === 'function') {
10727        const confirmLogout = await window.ChatNotification.confirmLogoutWithPushNotification();
10728        if (!confirmLogout) {
10729          return; // User cancelled logout
10730        }
10731      }
10732      
10733      const getCsrfToken = () => document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
10734      
10735      try {
10736        const response = await fetch('/auth/logout', {
10737          method: 'POST',
10738          credentials: 'same-origin',
10739          headers: {
10740            'Content-Type': 'application/json',
10741            'X-CSRF-Token': getCsrfToken()
10742          },
10743          body: JSON.stringify({
10744            csrf_token: getCsrfToken()
10745          })
10746        });
10747        
10748        if (response.ok) {
10749          if (window.guestCart && typeof window.guestCart.clear === 'function') {
10750            window.guestCart.clear();
10751          }
10752          window.location.href = '/login?logged_out=1';
10753        } else {
10754          console.error('Logout failed');
10755          window.location.href = '/logout';
10756        }
10757      } catch (error) {
10758        console.error('Logout error:', error);
10759        window.location.href = '/logout';
10760      }
10761    });
10762  }
10763
10764  const logoutBtn = document.getElementById('logoutBtn');
10765  if (logoutBtn) {
10766    logoutBtn.addEventListener('click', async function(e) {
10767      e.preventDefault();
10768      
10769      // Check push notification before logout
10770      if (window.ChatNotification && typeof window.ChatNotification.confirmLogoutWithPushNotification === 'function') {
10771        const confirmLogout = await window.ChatNotification.confirmLogoutWithPushNotification();
10772        if (!confirmLogout) {
10773          return; // User cancelled logout
10774        }
10775      }
10776      
10777      const getCsrfToken = () => document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
10778      
10779      try {
10780        const response = await fetch('/auth/logout', {
10781          method: 'POST',
10782          credentials: 'same-origin',
10783          headers: {
10784            'Content-Type': 'application/json',
10785            'X-CSRF-Token': getCsrfToken()
10786          },
10787          body: JSON.stringify({
10788            csrf_token: getCsrfToken()
10789          })
10790        });
10791        
10792        if (response.ok) {
10793          if (window.guestCart && typeof window.guestCart.clear === 'function') {
10794            window.guestCart.clear();
10795          }
10796          
10797          window.location.href = '/login?logged_out=1';
10798        } else {
10799          console.error('Logout failed');
10800          window.location.href = '/logout';
10801        }
10802      } catch (error) {
10803        console.error('Logout error:', error);
10804        window.location.href = '/logout';
10805      }
10806    });
10807  }
10808
10809  const wishlistBtn = document.getElementById('wishlistBtn');
10810  const notifBtn = document.getElementById('notifBtn');
10811  
10812  if (wishlistBtn) {
10813    wishlistBtn.addEventListener('click', function(e) {
10814      e.preventDefault();
10815      alert('Fitur wishlist akan segera tersedia!');
10816    });
10817  }
10818  
10819  if (notifBtn) {
10820    notifBtn.addEventListener('click', function(e) {
10821      e.preventDefault();
10822      alert('Fitur notifikasi akan segera tersedia!');
10823    });
10824  }
10825})();
10826
10827// ========== MOBILE BOTTOM NAVIGATION ==========
10828(function initBottomNav() {
10829  // Sync cart badge with bottom nav
10830  function syncBottomCartBadge() {
10831    const mainBadge = document.getElementById('cartCount');
10832    const bottomBadge = document.getElementById('bottomCartCount');
10833    if (mainBadge && bottomBadge) {
10834      const count = mainBadge.textContent;
10835      bottomBadge.textContent = count;
10836      bottomBadge.style.display = parseInt(count) > 0 ? 'flex' : 'none';
10837    }
10838  }
10839
10840  // Sync compare badge with bottom nav
10841  function syncBottomCompareBadge() {
10842    const mainBadge = document.getElementById('compareCount');
10843    const bottomBadge = document.getElementById('bottomCompareCount');
10844    if (mainBadge && bottomBadge) {
10845      const count = mainBadge.textContent;
10846      bottomBadge.textContent = count;
10847      bottomBadge.style.display = parseInt(count) > 0 ? 'flex' : 'none';
10848    }
10849  }
10850
10851  // Initial sync
10852  syncBottomCartBadge();
10853  syncBottomCompareBadge();
10854
10855  // Observe badge changes
10856  const observer = new MutationObserver(function() {
10857    syncBottomCartBadge();
10858    syncBottomCompareBadge();
10859  });
10860
10861  const cartBadge = document.getElementById('cartCount');
10862  const compareBadge = document.getElementById('compareCount');
10863  
10864  if (cartBadge) observer.observe(cartBadge, { childList: true, characterData: true, subtree: true });
10865  if (compareBadge) observer.observe(compareBadge, { childList: true, characterData: true, subtree: true });
10866
10867  // Bottom Cart Button
10868  const bottomCartBtn = document.getElementById('bottomCartBtn');
10869  if (bottomCartBtn) {
10870    bottomCartBtn.addEventListener('click', function(e) {
10871      e.preventDefault();
10872      
10873      const checkLogin = () => {
10874        return window.isUserLoggedIn ? window.isUserLoggedIn() : false;
10875      };
10876      
10877      if (checkLogin()) {
10878        window.location.href = '/keranjang';
10879      } else {
10880        showCartLoginModal();
10881      }
10882    });
10883  }
10884
10885  // Bottom Compare Button
10886  const bottomCompareBtn = document.getElementById('bottomCompareBtn');
10887  if (bottomCompareBtn) {
10888    bottomCompareBtn.addEventListener('click', function(e) {
10889      e.preventDefault();
10890      
10891      // Close chat widget if open
10892      if (window.plazaChat && window.plazaChat.isWidgetOpen) {
10893        window.plazaChat.closeWidget();
10894      }
10895      
10896      const mainCompareBtn = document.getElementById('compareBtn');
10897      if (mainCompareBtn) {
10898        mainCompareBtn.click();
10899      }
10900    });
10901  }
10902
10903  // Bottom Category Button - triggers mega menu
10904  const bottomCategoryBtn = document.getElementById('bottomCategoryBtn');
10905  if (bottomCategoryBtn) {
10906    bottomCategoryBtn.addEventListener('click', function(e) {
10907      e.preventDefault();
10908      // Trigger desktop mega menu
10909      const desktopTrigger = document.querySelector('.mm2-trigger:not(#bottomCategoryBtn)');
10910      if (desktopTrigger) {
10911        desktopTrigger.click();
10912      }
10913    });
10914  }
10915
10916  // Bottom Login Button
10917  const bottomLoginBtn = document.getElementById('bottomLoginBtn');
10918  if (bottomLoginBtn) {
10919    bottomLoginBtn.addEventListener('click', function(e) {
10920      e.preventDefault();
10921      window.location.href = '/login';
10922    });
10923  }
10924
10925  // Bottom Chat Button
10926  const bottomChatBtn = document.getElementById('bottomChatBtn');
10927  if (bottomChatBtn) {
10928    bottomChatBtn.addEventListener('click', function(e) {
10929      e.preventDefault();
10930      
10931      // Trigger chat widget (try multiple times if needed)
10932      const triggerChat = () => {
10933        const chatToggle = document.getElementById('chat-toggle-btn');
10934        if (chatToggle) {
10935          chatToggle.click();
10936          return true;
10937        }
10938        return false;
10939      };
10940      
10941      // Immediate attempt
10942      if (!triggerChat()) {
10943        // Retry after 100ms if not found
10944        setTimeout(() => {
10945          if (!triggerChat()) {
10946            // Last resort: manually show popup
10947            const popup = document.getElementById('chat-popup');
10948            if (popup) {
10949              popup.style.display = 'block';
10950            }
10951          }
10952        }, 100);
10953      }
10954    });
10955  }
10956
10957  // Sync chat unread badge
10958  function syncBottomChatBadge() {
10959    const chatBadge = document.getElementById('chat-unread-badge');
10960    const bottomChatBadge = document.getElementById('bottomChatCount');
10961    if (chatBadge && bottomChatBadge) {
10962      const count = chatBadge.textContent;
10963      bottomChatBadge.textContent = count;
10964      bottomChatBadge.style.display = chatBadge.style.display;
10965    }
10966  }
10967
10968  // Observe chat badge changes
10969  const chatBadge = document.getElementById('chat-unread-badge');
10970  if (chatBadge) {
10971    observer.observe(chatBadge, { attributes: true, childList: true, characterData: true, subtree: true });
10972    syncBottomChatBadge();
10973  }
10974
10975  // Active state management based on current page
10976  function updateActiveState() {
10977    const currentPath = window.location.pathname;
10978    const bottomNavItems = document.querySelectorAll('.bottom-nav-item');
10979    
10980    bottomNavItems.forEach(item => {
10981      item.classList.remove('active');
10982      
10983      const itemId = item.id;
10984      const page = item.getAttribute('data-page');
10985      const href = item.getAttribute('href');
10986      
10987      // Check by ID for specific pages
10988      if (itemId === 'bottomHomeBtn' && currentPath === '/') {
10989        item.classList.add('active');
10990      } else if (itemId === 'bottomCartBtn' && (currentPath === '/keranjang' || currentPath === '/cart')) {
10991        item.classList.add('active');
10992      } else if (itemId === 'bottomProfileBtn' && currentPath.startsWith('/profile')) {
10993        item.classList.add('active');
10994      } else if (itemId === 'bottomCategoryBtn' && currentPath.startsWith('/kategori')) {
10995        item.classList.add('active');
10996      } else if (itemId === 'bottomCompareBtn' && currentPath === '/compare') {
10997        item.classList.add('active');
10998      } else if (itemId === 'bottomChatBtn' && currentPath === '/chat') {
10999        item.classList.add('active');
11000      }
11001      // Fallback: check by data-page attribute
11002      else if (page === 'home' && currentPath === '/') {
11003        item.classList.add('active');
11004      } else if (page === 'profile' && currentPath.startsWith('/profile')) {
11005        item.classList.add('active');
11006      }
11007      // Fallback: check by href
11008      else if (href && currentPath === href) {
11009        item.classList.add('active');
11010      }
11011    });
11012  }
11013
11014  // Update active state on load
11015  updateActiveState();
11016
11017  // Bottom nav always visible - auto-hide disabled for better UX
11018  // const bottomNav = document.querySelector('.bottom-nav');
11019  // if (bottomNav && window.innerWidth <= 640) {
11020  //   // Auto-hide feature disabled
11021  // }
11022})();
11023// Cart Login Modal Handler (Global scope)
11024function showCartLoginModal() {
11025  const modal = document.getElementById('cartLoginModal');
11026  if (!modal) return;
11027  
11028  modal.classList.add('show');
11029  
11030  const confirmBtn = document.getElementById('cartLoginConfirm');
11031  const registerBtn = document.getElementById('cartRegisterBtn');
11032  const cancelBtn = document.getElementById('cartLoginCancel');
11033  
11034  const handleLogin = () => {
11035    window.location.href = '/login?return=' + encodeURIComponent('/keranjang');
11036  };
11037  
11038  const handleRegister = () => {
11039    window.location.href = '/register?return=' + encodeURIComponent('/keranjang');
11040  };
11041  
11042  const handleCancel = () => {
11043    modal.classList.remove('show');
11044    confirmBtn.removeEventListener('click', handleLogin);
11045    registerBtn.removeEventListener('click', handleRegister);
11046    cancelBtn.removeEventListener('click', handleCancel);
11047  };
11048  
11049  confirmBtn.removeEventListener('click', handleLogin);
11050  registerBtn.removeEventListener('click', handleRegister);
11051  cancelBtn.removeEventListener('click', handleCancel);
11052  
11053  confirmBtn.addEventListener('click', handleLogin);
11054  registerBtn.addEventListener('click', handleRegister);
11055  cancelBtn.addEventListener('click', handleCancel);
11056  
11057  modal.addEventListener('click', (e) => {
11058    if (e.target === modal) {
11059      handleCancel();
11060    }
11061  });
11062}
11063</script>
vendor: 1 bytes, line 11063
11063
11064<script data-inlined="cart-badge.js">
11065(function(){
11066  function selBadge(){
11067    return document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
11068  }
11069  function setBadge(n){
11070    const el = selBadge();
11071    if (!el) return;
11072    const count = Math.max(0, parseInt(n||0,10) || 0);
11073    el.textContent = String(count);
11074    // Sembunyikan jika 0? Jika ingin selalu terlihat, hapus 2 baris di bawah
11075    if (count <= 0) { el.classList.add('is-empty'); }
11076    else { el.classList.remove('is-empty'); }
11077  }
11078  async function refresh(){
11079    try{
11080      // Primary: Use server-side endpoint (handles both logged and guest users)
11081      const res = await fetch('/api/cart/get-simple.php', { credentials:'same-origin' });
11082      if (res.ok) {
11083        const data = await res.json();
11084        if (data && data.ok) {
11085          setBadge(data.items_count || 0);
11086          return;
11087        }
11088      }
11089      
11090      // Fallback hanya untuk guest user: check localStorage
11091      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
11092                        document.body.classList.contains('logged-in') ||
11093                        window.userLoggedIn === true;
11094      
11095      if (!isLoggedIn && window.guestCart && typeof window.guestCart.getItemsCount === 'function') {
11096        setBadge(window.guestCart.getItemsCount());
11097      }
11098    }catch(e){ 
11099      // Final fallback hanya untuk guest
11100      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true';
11101      if (!isLoggedIn) {
11102        try {
11103          const guestCartData = JSON.parse(localStorage.getItem('plazait_guest_cart') || '{"items":{}}');
11104          const items = guestCartData.items || {};
11105          const count = Object.values(items).reduce((total, item) => total + (item.qty || 0), 0);
11106          setBadge(count);
11107        } catch(e2) {
11108          setBadge(0);
11109        }
11110      } else {
11111        setBadge(0);
11112      }
11113    }
11114  }
11115
11116  // Ekspos untuk dipanggil dari skrip lain (opsional)
11117  window.CartBadge = { set: setBadge, refresh };
11118  window.updateCartCounter = refresh; // Alias for compatibility
11119
11120  // Update saat halaman load
11121  if (document.readyState === 'loading') {
11122    document.addEventListener('DOMContentLoaded', refresh);
11123  } else {
11124    refresh();
11125  }
11126
11127  // Dengarkan event global dari add/remove/update
11128  document.addEventListener('cart:updated', function(ev){
11129    const c = ev && ev.detail && typeof ev.detail.items_count === 'number'
11130      ? ev.detail.items_count : null;
11131    if (c !== null) setBadge(c);
11132    else refresh();
11133  });
11134
11135  // Dengarkan event khusus cart remove untuk update badge
11136  document.addEventListener('cart:removed', function(ev){
11137    // Langsung refresh untuk memastikan data terbaru
11138    refresh();
11139  });
11140
11141  // Jika addToCart sudah ada sebelum file ini, coba wrap agar badge ter-update.
11142  const prevAdd = window.addToCart;
11143  if (typeof prevAdd === 'function'){
11144    window.addToCart = async function(pid, qty, vid, meta){
11145      const res = await prevAdd(pid, qty, vid, meta);
11146      try{
11147        const cnt = res && res.cart ? res.cart.items_count : null;
11148        if (cnt !== null) setBadge(cnt);
11149        // tetap siarkan event agar listener lain ikut sync
11150        document.dispatchEvent(new CustomEvent('cart:updated', { detail: res.cart || {} }));
11151      }catch(_){}
11152      return res;
11153    };
11154  }
11155
11156  // Helper function to check if user is logged in
11157  function isUserLoggedIn() {
11158    const metaTag = document.querySelector('meta[name="user-logged-in"]');
11159    return metaTag ? metaTag.getAttribute('content') === 'true' : false;
11160  }
11161
11162  // Make login check globally accessible
11163  window.isUserLoggedIn = isUserLoggedIn;
11164})();
11165</script>
11165
11166
11167<!-- Product Compare Sidebar -->
11168<div class="compare-sidebar" id="compareSidebar">
11169  <div class="compare-header">
11170    <h4 class="compare-title">
11171      <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
11172        <path d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"/>
11173      </svg>
11174      Bandingkan Produk
11175      <span class="compare-count" id="compareCountSidebar">(0)</span>
11176    </h4>
11177    <button class="compare-close" id="compareClose" aria-label="Tutup">
11178      <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
11179        <path d="M18.3 5.7a1 1 0 0 0-1.4 0L12 10.6 7.1 5.7a1 1 0 1 0-1.4 1.4L10.6 12l-4.9 4.9a1 1 0 0 0 1.4 1.4L12 13.4l4.9 4.9a1 1 0 0 0 1.4-1.4L13.4 12l4.9-4.9a1 1 0 0 0 0-1.4z"/>
11180      </svg>
11181    </button>
11182  </div>
11183  
11184  <div class="compare-content">
11185    <div class="compare-empty" id="compareEmpty">
11186      <div class="empty-icon">
11187        <svg width="48" height="48" viewBox="0 0 24 24" fill="currentColor" opacity="0.3">
11188          <path d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"/>
11189        </svg>
11190      </div>
11191      <h5>Belum ada produk dipilih</h5>
11192      <p>Pilih produk yang ingin dibandingkan dengan klik tombol "Bandingkan Produk" di halaman produk.</p>
11193    </div>
11194    
11195    <div class="compare-list" id="compareList">
11196      <!-- Products will be populated here -->
11197    </div>
11198  </div>
11199  
11200  <div class="compare-actions" id="compareActions">
11201    <button class="btn btn-outline-danger btn-sm" id="compareClearAll">
11202      <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
11203        <path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
11204      </svg>
11205      Hapus Semua
11206    </button>
11207    <button class="btn btn-primary btn-sm" id="compareAnalyze" disabled>
11208      <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
11209        <path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z"/>
11210      </svg>
11211      Analisis AI
11212    </button>
11213  </div>
11214  
11215  <!-- AI Disclaimer in Sidebar -->
11216  <div class="compare-disclaimer">
11217    <div class="ai-disclaimer">
11218      <div class="disclaimer-text">
11219        <strong>💡 Catatan:</strong> Analisis ini dihasilkan secara otomatis dan dapat memiliki keterbatasan.
11220        Gunakan sebagai referensi tambahan dengan tetap mempertimbangkan kebutuhan Anda.
11221        Untuk informasi lebih lanjut, silakan hubungi tim IWANTOGEL.
11222      </div>
11223    </div>
11224  </div>
11225</div><style data-inlined="pd-lightbox.css">
11226/* PlazaIT Product Lightbox (vanilla, no CDN) — v4 */
11227.pd-lb {
11228  position: fixed; inset: 0; z-index: 99999;
11229  background: rgba(0,0,0,.95);
11230  display: none;
11231  color: #fff;
11232  /* Transition untuk swipe-down feedback */
11233  transition: transform .18s ease, opacity .18s ease;
11234}
11235.pd-lb.show { display: block; }
11236
11237/* Canvas di bawah (z:1), Controls di atas (z:2) */
11238.pd-lb-canvas {
11239  position: absolute; inset: 56px 0 56px 0;
11240  display: flex; align-items: center; justify-content: center;
11241  overflow: hidden;
11242  touch-action: none;
11243  z-index: 1;
11244}
11245
11246.pd-lb-img {
11247  max-width: 96vw; max-height: 86vh;
11248  will-change: transform;
11249  user-select: none;
11250  pointer-events: none;
11251  transform-origin: 50% 50%;
11252}
11253
11254.pd-lb-close, .pd-lb-prev, .pd-lb-next, .pd-lb-counter {
11255  position: absolute;
11256  z-index: 2;
11257}
11258
11259.pd-lb-close, .pd-lb-prev, .pd-lb-next {
11260  background: rgba(17,24,39,.7);
11261  color: #fff; border: 0; cursor: pointer;
11262  width: 40px; height: 40px; border-radius: 999px;
11263  display: inline-flex; align-items: center; justify-content: center;
11264  transition: background-color .15s ease, transform .05s ease;
11265  -webkit-tap-highlight-color: transparent;
11266}
11267.pd-lb-close:hover, .pd-lb-prev:hover, .pd-lb-next:hover { background: rgba(31,41,55,.85); }
11268.pd-lb-close:active, .pd-lb-prev:active, .pd-lb-next:active { transform: translateY(.5px); }
11269
11270.pd-lb-close { top: max(12px, env(safe-area-inset-top, 12px)); right: max(12px, env(safe-area-inset-right, 12px)); }
11271.pd-lb-prev  { top: 50%; left:  10px; transform: translateY(-50%); }
11272.pd-lb-next  { top: 50%; right: 10px; transform: translateY(-50%); }
11273
11274.pd-lb-counter {
11275  top: max(16px, env(safe-area-inset-top, 16px)); left: 50%;
11276  transform: translateX(-50%);
11277  font-weight: 700; font-size: 13px; letter-spacing: .4px;
11278  background: rgba(17,24,39,.65);
11279  padding: 6px 10px; border-radius: 999px; border: 1px solid rgba(0,0,0,.1);
11280}
11281
11282@media (max-width: 640px){
11283  .pd-lb-img { max-width: 98vw; max-height: 80vh; }
11284  .pd-lb-prev, .pd-lb-next { width: 36px; height: 36px; }
11285  .pd-lb-close { width: 38px; height: 38px; }
11286}
11287</style>
11288<style>.paylater-promo{display:inline-flex;align-items:center;gap:8px;background:linear-gradient(135deg,#f0fdf4 0,#dcfce7 100%);border:1px solid #86efac;border-radius:8px;padding:8px 12px;font-size:13px;line-height:1.4;margin:8px 0}.paylater-promo-sm{padding:6px 10px;font-size:11px;gap:6px}.paylater-promo-md{padding:8px 12px;font-size:13px;gap:8px}.paylater-promo-lg{padding:10px 16px;font-size:14px;gap:10px}.paylater-icon{font-size:1.2em;flex-shrink:0}.paylater-text{display:flex;flex-direction:column;gap:2px}.paylater-text strong{color:#15803d;font-weight:600}.paylater-text small{color:#16a34a;font-size:.85em;opacity:.9}.paylater-desktop-only{display:none}.paylater-mobile-only{display:flex}@media (min-width:641px){.paylater-desktop-only{display:flex}.paylater-mobile-only{display:none}}@media (max-width:640px){.paylater-promo{font-size:12px;padding:7px 10px}.paylater-promo-lg{font-size:13px;padding:8px 12px}}@media print{.paylater-promo{display:none!important}}.paylater-compact{display:inline-block;font-size:11px;font-weight:600;color:#059669;background:#d1fae5;padding:3px 8px;border-radius:4px;margin:6px 0 4px;line-height:1.3}@media (max-width:640px){.paylater-compact{font-size:10px;padding:2px 6px;margin:5px 0 3px}}@media print{.paylater-compact{display:none!important}}</style><style nonce="se3iyzEHMc9FQEB4wSyd0w==">.pd-voucher-minimal{margin:10px 0;display:flex;flex-direction:column;gap:6px}.pdv-min-item{display:flex;align-items:center;gap:8px;padding:0;height:32px;transition:all .2s ease}.pdv-min-hidden{display:none}.pdv-min-disabled{opacity:.5}.pdv-badge-btn{display:inline-flex;align-items:center;gap:8px;background:linear-gradient(135deg,#667eea 0,#764ba2 100%);border:none;border-radius:8px;padding:3px;
11288cursor:pointer;transition:all .2s ease;width:auto}.pdv-badge-btn:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 4px 12px rgba(102,126,234,.3)}.pdv-badge-btn:disabled{opacity:.6;cursor:not-allowed}.pdv-badge-icon{color:#fff;flex-shrink:0}.pdv-badge-text{font-size:14px;font-weight:700;color:#fff;white-space:nowrap}.pdv-badge-code{font-family:'Courier New',monospace;font-size:13px;font-weight:700;color:#fff;background:rgba(0,0,0,.2);padding:4px 8px;border-radius:4px;border:none;white-space:nowrap}.pdv-min-copy,.pdv-min-more{padding:4px 12px;font-size:12px;font-weight:600;border-radius:5px;border:1px solid #e2e8f0;background:#fff;color:#667eea;cursor:pointer;transition:all .2s ease;white-space:nowrap}.pdv-min-copy:hover:not(:disabled),.pdv-min-more:hover{background:#667eea;color:#fff;border-color:#667eea}.pdv-min-copy:disabled{opacity:.4;cursor:not-allowed}.pdv-min-copy.copied{background:#22c55e;color:#fff;border-color:#22c55e}.pdv-min-more{margin-left:auto}.modal-close{position:absolute;top:20px;right:20px;background:none;border:none;cursor:pointer;color:#64748b;padding:4px;transition:color .2s ease}.modal-close:hover{color:#1e293b}.modal-body{margin-bottom:8px}.voucher-modal-content{text-align:center}.voucher-modal-badge{display:inline-flex;flex-direction:row;align-items:center;gap:6px;background:linear-gradient(135deg,#667eea 0,#764ba2 100%);border-radius:10px;padding:8px 16px;margin-bottom:12px}.vmb-value{font-size:18px;font-weight:700;color:#fff;line-height:1}.vmb-label{font-size:18px;font-weight:700;color:#fff;text-transform:uppercase;letter-spacing:.5px}.voucher-modal-code{font-family:'Courier New',monospace;font-size:14px;font-weight:700;color:#667eea;background:rgba(102,126,234,.1);padding:6px 12px;border-radius:6px;border:2px dashed rgba(102,126,234,.3);display:inline-block;margin-bottom:10px}.voucher-modal-name{font-size:14px;font-weight:600;color:#1e293b;margin-bottom:16px}.voucher-modal-details{text-align:left;background:#f8fafc;border-radius:8px;padding:12px;margin-bottom:16px}.vmd-item{display:flex;align-items:flex-start;gap:8px;padding:6px 0;font-size:13px;color:#64748b;border-bottom:1px solid #e2e8f0}.vmd-item:last-child{border-bottom:none}.vmd-item svg{color:#667eea;flex-shrink:0;margin-top:2px}.vmd-item strong{color:#1e293b}.modal-actions{padding:20px}.modal-btn-primary{background:linear-gradient(135deg,#667eea 0,#764ba2 100%);color:#fff}.modal-btn-primary:hover{opacity:.9;transform:translateY(-1px)}@media (max-width:640px){.pdv-min-item{gap:6px}.pdv-badge-btn{padding:3px;gap:6px}.pdv-badge-text{font-size:13px}.pdv-badge-code{font-size:12px;padding:3px 6px}.pdv-min-copy,.pdv-min-more{padding:3px 10px;font-size:11px}.modal-dialog{max-width:95%;margin:20px}.vmb-value{font-size:16px}.vmb-label{font-size:16px}.voucher-modal-code{font-size:13px;padding:5px 10px}}</style><style nonce="se3iyzEHMc9FQEB4wSyd0w==">.promo-endyear-mini-banner{display:flex;align-items:center;gap:12px;padding:12px 16px;background:rgba(0,0,0,.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:2px solid rgba(242,140,26,.2);border-radius:12px;margin:16px 0;box-shadow:0 4px 16px rgba(0,0,0,.08);transition:all .3s ease}.promo-endyear-mini-banner:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(0,0,0,.12);border-color:rgba(242,140,26,.4)}.pey-icon{font-size:32px;line-height:1;flex-shrink:0}.pey-content{flex:1;min-width:0}.pey-content strong{display:block;font-size:14px;font-weight:700;color:#F28C1A;margin-bottom:2px}.pey-content span{display:block;font-size:13px;color:#64748b;line-height:1.4}.pey-link{flex-shrink:0;padding:8px 16px;background:linear-gradient(135deg,#F28C1A,#FFB84D);color:#fff;text-decoration:none;font-size:13px;font-weight:700;border-radius:8px;white-space:nowrap;transition:all .2s ease;box-shadow:0 2px 8px rgba(242,140,26,.25)}.pey-link:hover{transform:translateX(2px);box-shadow:0 4px 12px rgba(242,140,26,.35)}@media(max-width:640px){.promo-endyear-mini-banner{flex-wrap:wrap;gap:8px;padding:10px 12px}.pey-icon{font-size:28px;align-self:flex-start}.pey-content{flex:1;min-width:0}.pey-content strong{font-size:12px}.pey-content span{font-size:11px;line-height:1.3}.pey-link{width:100%;flex-basis:100%;font-size:11px;padding:6px 12px;text-align:center}}@media print{.promo-endyear-mini-banner{display:none!important}}</style>
11289<style nonce="se3iyzEHMc9FQEB4wSyd0w==">
11290.pd-title-row{position:relative;margin-bottom:12px;padding-right:52px}.pd-title{margin:0!important}.pd-share-btn{position:absolute;top:0;right:0;width:40px;height:40px;
11290display:inline-flex;align-items:center;justify-content:center;border-radius:10px;border:1px solid #e2e8f0;background:#fff;color:#64748b;cursor:pointer;transition:all .2s ease;-webkit-tap-highlight-color:transparent}.pd-share-btn:hover{background:#f8fafc;border-color:#cbd5e1;color:#0f172a;transform:translateY(-1px)}.pd-share-btn:active{transform:translateY(0)}.pd-share-btn svg{display:block}
11291
11292.pd-share-modal-overlay{position:fixed;inset:0;background:rgba(15,23,42,.75);backdrop-filter:blur(4px);z-index:99999;display:none;align-items:center;justify-content:center;padding:0;animation:fadeIn .2s ease}.pd-share-modal-overlay.show{display:flex}.pd-share-modal{background:#fff;border-radius:20px;box-shadow:0 25px 50px -12px rgba(0,0,0,.25);max-width:480px;width:100%;max-height:90vh;overflow:hidden;display:flex;flex-direction:column;animation:slideUp .3s cubic-bezier(.16,1,.3,1)}@keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes slideUp{from{opacity:0;transform:translateY(20px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}
11293
11294.pd-share-header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px;border-bottom:1px solid #f1f5f9}.pd-share-title{font-size:18px;font-weight:700;color:#0f172a;margin:0}.pd-share-close{width:32px;height:32px;border-radius:8px;border:0;background:transparent;color:#64748b;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:all .15s ease}.pd-share-close:hover{background:#f1f5f9;color:#0f172a}.pd-share-body{padding:24px;overflow-y:auto}
11295
11296.pd-share-preview{display:flex;gap:12px;padding:16px;background:#f8fafc;border-radius:12px;margin-bottom:24px}.pd-share-preview-img{width:64px;height:64px;border-radius:8px;object-fit:cover;flex-shrink:0;border:1px solid #e2e8f0}.pd-share-preview-info{flex:1;min-width:0}.pd-share-preview-name{font-size:14px;font-weight:600;color:#0f172a;margin:0 0 4px 0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.pd-share-preview-price{font-size:16px;font-weight:700;color:#0ea5e9;margin:0}
11297
11298.pd-share-label{font-size:13px;font-weight:600;color:#475569;margin:0 0 12px 0;text-transform:uppercase;letter-spacing:.5px}.pd-share-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:24px}.pd-share-option{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px 8px;border:1.5px solid #e2e8f0;border-radius:12px;background:#fff;cursor:pointer;transition:all .2s ease;text-decoration:none;color:inherit}.pd-share-option:hover{border-color:#94a3b8;background:#f8fafc;transform:translateY(-2px)}.pd-share-option:active{transform:translateY(0)}
11299
11300.pd-share-icon{width:40px;height:40px;border-radius:10px;display:inline-flex;align-items:center;justify-content:center;position:relative}.pd-share-icon svg{width:24px;height:24px;display:block}.pd-share-icon-whatsapp{background:linear-gradient(135deg,#25D366 0%,#128C7E 100%);color:#fff}.pd-share-icon-facebook{background:linear-gradient(135deg,#1877F2 0%,#0c63d4 100%);color:#fff}.pd-share-icon-twitter{background:linear-gradient(135deg,#1DA1F2 0%,#0d8bd9 100%);color:#fff}.pd-share-icon-telegram{background:linear-gradient(135deg,#0088cc 0%,#0077b3 100%);color:#fff}.pd-share-icon-email{background:linear-gradient(135deg,#EA4335 0%,#c5331f 100%);color:#fff}.pd-share-icon-copy{background:linear-gradient(135deg,#f1db63 0%,#e5d146 100%);color:#fff}.pd-share-icon-pdf{background:linear-gradient(135deg,#ef4444 0%,#dc2626 100%);color:#fff}.pd-share-name{font-size:12px;font-weight:600;color:#475569;text-align:center}
11301
11302.pd-share-copy-section{background:#f8fafc;padding:16px;border-radius:12px}.pd-share-copy-input-wrap{display:flex;gap:8px}.pd-share-copy-input{flex:1;padding:12px 14px;border:1px solid #e2e8f0;border-radius:10px;font-size:14px;color:#0f172a;background:#fff;font-family:'Segoe UI',system-ui,sans-serif}.pd-share-copy-input:focus{outline:0;border-color:#0ea5e9;box-shadow:0 0 0 3px rgba(14,165,233,.1)}.pd-share-copy-btn{padding:12px 20px;border:0;border-radius:10px;background:linear-gradient(135deg,#0ea5e9 0%,#0284c7 100%);color:#fff;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s ease;white-space:nowrap;display:inline-flex;align-items:center;gap:6px}.pd-share-copy-btn:hover{background:linear-gradient(135deg,#0284c7 0%,#0369a1 100%);transform:translateY(-1px);box-shadow:0 4px 12px rgba(14,165,233,.3)}
11302.pd-share-copy-btn:active{transform:translateY(0)}.pd-share-copy-btn svg{width:16px;height:16px}.pd-share-copy-btn.copied{background:linear-gradient(135deg,#22c55e 0%,#16a34a 100%);pointer-events:none}
11303
11304@media(max-width:640px){.pd-share-modal{border-radius:20px 20px 0 0;margin-top:auto;max-height:85vh}.pd-share-grid{grid-template-columns:repeat(3,1fr)}.pd-share-header{padding:16px 20px}.pd-share-body{padding:20px}.pd-share-btn{width:36px;height:36px}.pd-share-btn svg{width:18px;height:18px}}@media(max-width:480px){.pd-share-grid{grid-template-columns:repeat(3,1fr);gap:10px}.pd-share-option{padding:12px 6px}.pd-share-icon{width:36px;height:36px}.pd-share-icon svg{width:20px;height:20px}.pd-share-name{font-size:11px}}
11305
11306.mf-share{background:#fff;color:#fb923c;border:2px solid #fb923c;display:inline-flex;align-items:center;justify-content:center}.mf-share:active{transform:scale(.95)}.mf-share svg{display:block}
11307
11308@media(max-width:640px){.pd-share-btn{display:none!important}}
11309
11310.stock-badge.in{background:#e6f9ec;color:#15803d;border-radius:999px;padding:4px 10px;font-weight:700}.stock-badge.out{background:#821603;color:#ffffff;border-radius:999px;padding:4px 10px;font-weight:700}
11311
11312.stock-info-btn{--c:#0ea5e9;--b:#bae6fd;--bg:#f0f9ff;--bg-h:#e0f2fe;--b-h:#0ea5e9;margin-left:0;display:inline-flex;align-items:center;justify-content:center;gap:0.5rem;padding:4px 10px;border-radius:999px;border:1.5px solid var(--b);background:var(--bg);color:var(--c);cursor:pointer;font-size:12px;font-weight:600;transition:all .2s ease;-webkit-tap-highlight-color:transparent}.stock-info-btn:hover{background:var(--bg-h);border-color:var(--b-h);color:#0284c7;transform:translateY(-1px);box-shadow:0 2px 8px rgba(14,165,233,.15)}.stock-info-btn:active{transform:translateY(0)}.stock-info-btn:focus-visible{outline:0;box-shadow:0 0 0 3px rgba(14,165,233,.2)}.stock-info-btn svg{width:14px;height:14px;flex-shrink:0}.stock-info-btn .btn-text{white-space:nowrap}
11313
11314.stock-popover{position:absolute;z-index:9999;min-width:280px;max-width:92vw;background:#000000;color:#eef3ff;border:1px solid rgba(0,0,0,.08);border-radius:14px;box-shadow:0 20px 40px rgba(12,12,20,.45);padding:12px 12px 8px;display:none}.stock-popover.show{display:block}.stock-popover::before{content:"";position:absolute;top:-8px;left:24px;width:14px;height:14px;background:#000000;border-left:1px solid rgba(0,0,0,.08);border-top:1px solid rgba(0,0,0,.08);transform:rotate(45deg)}.sp-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.sp-title{font-weight:800;font-size:14px}.sp-close{background:transparent;border:0;color:#98a2b3;cursor:pointer}
11315
11316.sp-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.sp-item{display:grid;grid-template-columns:1fr auto 32px;column-gap:12px;align-items:center;padding:10px 10px;min-height:44px;border-radius:10px;background:rgba(0,0,0,.03)}.sp-left{display:flex;align-items:center;gap:10px}.sp-dot{width:10px;height:10px;border-radius:999px;background:var(--dot,#60a5fa);display:inline-block}.sp-label{font-weight:600;font-size:13px}
11317
11318
11319.sp-qty{grid-column:2;justify-self:end;min-width:40px;text-align:right;font-weight:800;font-size:13px;color:#cfe4ff;font-variant-numeric:tabular-nums}
11320
11321.sp-wa{
11322  grid-column:3; justify-self:end;
11323  width:32px; height:32px; min-width:32px; min-height:32px;
11324  box-sizing:border-box;
11325  padding:0; line-height:0; text-decoration:none; outline:0;
11326  display:inline-flex; align-items:center; justify-content:center;
11327  border-radius:999px; border:1px solid rgba(37,211,102,.60);  /* 1px untuk hasil “crisp” */
11328  background:rgba(37,211,102,.08); color:#25D366;
11329  transition:background-color .15s ease, border-color .15s ease, transform .05s ease;
11330  -webkit-tap-highlight-color:transparent;
11331}
11332.sp-wa:hover{background:rgba(37,211,102,.16);border-color:rgba(37,211,102,.75)}
11333.sp-wa:active{transform:translateY(.5px) scale(.98)}
11334
11335.sp-wa svg{width:18px;height:18px;display:block;vertical-align:middle}
11336.stock-popover a{text-decoration:none}
11337
11338#deskripsi img{max-width:450px!important;height:unset!important}@media(max-width:640px){.stock-popover{position:fixed;left:0;right:0;bottom:max(12px,env(safe-area-inset-bottom,12px));margin:0 10px;padding:14px 12px 10px;border-radius:16px}.stock-popover::before{display:none}.stock-info-btn{padding:4px 8px;gap:4px;border:0;background:transparent}.stock-info-btn:hover{background:transparent;box-shadow:none;transform:none}.stock-info-btn .btn-text{display:none}.stock-info-btn svg{width:16px;height:16px}.sp-item{grid-template-columns:1fr auto 36px}.sp-wa{width:36px;height:36px;min-width:36px;min-height:36px}.sp-wa svg{width:19px;height:19px}}
11339
11340.stock-dot{position:absolute;bottom:8px;right:8px;width:8px;height:8px;border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,0.4);z-index:2;cursor:help}.stock-dot.ready{background:#10b981}.stock-dot.low{background:#f59e0b}.stock-dot.out{background:#ef4444}.stock-dot:hover{transform:scale(1.2);transition:transform 0.2s ease}@media(max-width:640px){.stock-dot{width:7px;height:7px;bottom:6px;right:6px}}
11341
11342.pd-related-card .spec-chips{font-size:11px;color:#64748b;margin:4px 0 6px 0;line-height:1.3;font-weight:500}.pd-related-card .spec-chips .spec-divider{margin:0 4px;opacity:0.6}@media(max-width:640px){.pd-related-card .spec-chips{font-size:10px;margin:3px 0 5px 0}#deskripsi img{max-width:300px!important;height:unset!important;justify-self:center!important}}
11343
11344.pwp-trigger-badge{display:inline-flex;align-items:center;gap:4px;padding:3px 8px 4px;background:linear-gradient(135deg,#fbbf24 0%,#f59e0b 100%);color:#78350f;border-radius:6px;font-size:.65rem;font-weight:600;border:0}.pwp-trigger-badge svg{flex-shrink:0;width:12px;height:12px}
11345.pwp-deals-section{margin:0;padding:12px;background:linear-gradient(135deg,#fef3c7 0%,#fde68a 100%);border-radius:12px;border:2px solid #fbbf24}.pwp-deals-header{display:flex;gap:10px;margin-bottom:10px}.pwp-icon{flex-shrink:0;color:#f59e0b;width:18px;height:18px}.pwp-text .pwp-title{margin:0 0 2px 0;font-size:14px;font-weight:700;color:#78350f}.pwp-text .pwp-subtitle{margin:0;font-size:11px;color:#92400e;opacity:0.9}.pwp-deals-loading{text-align:center;padding:40px 0;color:#92400e}.pwp-spinner{width:40px;height:40px;border:3px solid #fbbf24;border-top-color:#f59e0b;border-radius:50%;animation:spin 0.8s linear infinite;margin:0 auto 12px}@keyframes spin{to{transform:rotate(360deg)}}
11346.pwp-deals-grid{display:flex;flex-direction:column;gap:6px}.pwp-deal-card{background:#fff;border-radius:6px;overflow:hidden;border:1px solid #fbbf24;display:flex;align-items:center;padding:8px;gap:10px}.pwp-deal-card:hover{background:#fef3c7}.pwp-deal-thumb{flex-shrink:0;width:50px;height:50px;background:#f8fafc;overflow:hidden;border-radius:4px}.pwp-deal-thumb img{width:100%;height:100%;object-fit:cover}.pwp-deal-badge{background:#dc2626;color:#fff;padding:2px 5px;border-radius:3px;font-size:9px;font-weight:700;margin-left:auto}.pwp-deal-info{flex:1;display:flex;flex-direction:column;gap:4px}.pwp-deal-name{font-size:12px;font-weight:600;color:#0f172a;margin:0;line-height:1.2}.pwp-deal-prices{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.pwp-deal-price{font-size:14px;font-weight:800;color:#dc2626;white-space:nowrap}.pwp-deal-original{font-size:11px;color:#64748b;text-decoration:line-through;white-space:nowrap}.pwp-cart-note{margin-top:8px;padding:6px 10px;background:#e0f2fe;border-left:2px solid#0ea5e9;font-size:11px;color:#0c4a6e;border-radius:4px}
11347@media(max-width:640px){.pwp-deals-section{margin:12px 0;padding:10px;border-radius:10px}.pwp-deals-header{gap:8px}.pwp-text .pwp-title{font-size:13px}.pwp-text .pwp-subtitle{font-size:10px}.pwp-deal-card{padding:6px;gap:8px}.pwp-deal-thumb{width:45px;height:45px}.pwp-deal-name{font-size:11px}.pwp-deal-price{font-size:13px}.pwp-deal-original{font-size:10px}}
11348
11349.preorder-badge{display:inline-flex;align-items:center;gap:5px;padding:6px 12px;background:linear-gradient(135deg,#8b5cf6 0%,#7c3aed 100%);color:#fff;border-radius:8px;font-size:13px;font-weight:700;border:0;box-shadow:0 2px 8px rgba(139,92,246,.25)}.preorder-badge svg{width:16px;height:16px;flex-shrink:0}.preorder-info{margin:12px 0;padding:14px;background:linear-gradient(135deg,#f5f3ff 0%,#ede9fe 100%);border:1.5px solid #c4b5fd;border-radius:12px}.preorder-info-header{display:flex;align-items:center;gap:8px;margin-bottom:10px}.preorder-info-icon{width:20px;height:20px;color:#7c3aed;flex-shrink:0}.preorder-info-title{font-size:14px;font-weight:700;color:#5b21b6;margin:0}.preorder-info-items{display:flex;flex-direction:column;gap:8px}.preorder-info-row{display:flex;align-items:center;justify-content:space-between;font-size:13px}.preorder-info-label{color:#6b21a8;font-weight:600}.preorder-info-value{color:#5b21b6;font-weight:700}.preorder-info-value.highlight{color:#dc2626;font-size:14px}.preorder-info-note{margin-top:10px;padding:8px 10px;background:#fef3c7;border-left:2px solid #f59e0b;font-size:12px;color:#78350f;border-radius:6px;line-height:1.4}@media(max-width:640px){.preorder-badge{font-size:12px;padding:5px 10px}.preorder-info{margin:10px 0;padding:12px}.preorder-info-title{font-size:13px}.preorder-info-row{font-size:12px}.preorder-info-note{font-size:11px;padding:6px 8px}}
11350</style>
11351
11352<div class="pd-sticky-header" id="pd-sticky-header"><div class="pd-sticky-header-container"><div class="pd-sticky-thumb"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="IWANTOGEL" id="pd-sticky-thumb-img"></div><div class="pd-sticky-info"><h2 class="pd-sticky-title" id="pd-sticky-title">LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bers
11352ama IWANTOGEL </h2><div class="pd-sticky-price-row"><span class="pd-sticky-price" id="pd-sticky-price">Rp 5.000,-</span><span class="pd-sticky-price-original" id="pd-sticky-price-original">Rp 1.000.000</span><span class="pd-sticky-discount" id="pd-sticky-discount">-99.5%</span></div></div><div class="pd-sticky-actions"><button type="button" class="pd-sticky-share-btn" id="pd-sticky-share-btn" aria-label="Bagikan produk" title="Bagikan"><svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true"><path fill="currentColor" d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/></svg></button><button type="button" class="pd-sticky-btn pd-sticky-btn-chat" id="pd-sticky-chat"><svg viewBox="0 0 640 640" fill="currentColor" aria-hidden="true"><path d="M320 128C241 128 175.3 185.3 162.3 260.7C171.6 257.7 181.6 256 192 256L208 256C234.5 256 256 277.5 256 304L256 400C256 426.5 234.5 448 208 448L192 448C139 448 96 405 96 352L96 288C96 164.3 196.3 64 320 64C443.7 64 544 164.3 544 288L544 456.1C544 522.4 490.2 576.1 423.9 576.1L336 576L304 576C277.5 576 256 554.5 256 528C256 501.5 277.5 480 304 480L336 480C362.5 480 384 501.5 384 528L384 528L424 528C463.8 528 496 495.8 496 456L496 435.1C481.9 443.3 465.5 447.9 448 447.9L432 447.9C405.5 447.9 384 426.4 384 399.9L384 303.9C384 277.4 405.5 255.9 432 255.9L448 255.9C458.4 255.9 468.3 257.5 477.7 260.6C464.7 185.3 399.1 127.9 320 127.9z"/></svg>Tanya</button><button type="button" class="pd-sticky-btn pd-sticky-btn-cart" id="pd-sticky-add-cart" disabled>+ Keranjang</button><button type="button" class="pd-sticky-btn pd-sticky-btn-buy" id="pd-sticky-buy" disabled>Beli Sekarang</button></div></div></div>
11353
11354<main class="product-single product-single--v3"><nav class="pd-breadcrumb" aria-label="breadcrumb"><a href="https://loklokpro.com/">IWANTOGEL</a><span>-</span><a href="https://loklokpro.com/">TOTO SLOT ONLINE</a><span>-</span><a href="https://loklokpro.com/">TOTO TOGEL ONLINE</a><span>-</span><a href="https://loklokpro.com/">SLOT GACOR</a><span>-</span><a href="https://loklokpro.com/">BONUS MELIMPAH</a><span>-</span><span class="current"> 
11355
11356</span></nav>
11357
11358<div class="pd-top-layout"><section class="pd-media-card"><figure class="pd-main-image" id="pd-main-figure"><img id="pd-main-img" src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOTO TOGEL" data-slug="" loading="eager">
11359</figure>
11360
11361<div class="pd-mobile-slider-wrapper">
11362<div class="pd-mobile-slider" id="pd-mobile-slider" aria-label="Galeri gambar (geser)"><div class="pd-mslide"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="eager"></div><div class="pd-mslide"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></div><div class="pd-mslide"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></div><div class="pd-mslide"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></div><div class="pd-mslide"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></div><div class="pd-mslide"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></div></div><div class="pd-gallery-counter" id="pd-gallery-counter">1/6</div></div>
11363<div class="pd-thumb-gallery" id="pd-thumb-gallery" aria-label="Galeri gambar produk"><button type="button" class="pd-thumb-btn active" data-full="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" data-alt="TOGEL ONLINE" data-slug="" aria-label="Gambar 1"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" data-alt
11363="TOGEL ONLINE" data-slug="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" aria-label="Gambar 2"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" data-alt="TOGEL ONLINE" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-223.webp" aria-label="Gambar 3"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" data-alt="TOGEL ONLINE" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-776.webp" aria-label="Gambar 4"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" data-alt="TOGEL ONLINE" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-634.webp" aria-label="Gambar 5"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" data-alt="TOGEL ONLINE" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-892.webp" aria-label="Gambar 6"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE" loading="lazy"></button></div></section><section class="pd-info-card"><div id="pd-variant-combined" class="pd-variant-combined" hidden></div><div class="pd-title-row">
11364        <h1 class="pd-title" id="pd-title">LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL </h1><button type="button" class="pd-share-btn" id="pd-share-btn" aria-label="Bagikan produk" title="Bagikan"><svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"><path fill="currentColor" d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/></svg></button></div>
11365<div id="pd-price-anchor">
11366</div>
11367<div class="pd-price-qr-container">
11368        <div class="pd-price-block" id="pd-price-block">
11369                <div class="pd-price-row">
11370                        <span class="pd-price-current" id="pd-price-current" >Rp 5.000</span>
11371                        <span class="pd-price-original" id="pd-price-original">Rp 50.000</span>
11372                        <span class="pd-discount-badge" id="pd-discount-badge">-90%</span></div>
11373                        <div class="pd-meta-row">
11374                                <span class="sold-inline" id="pd-sold-inline" data-role="sold-chip">Terjual Terjual 5</span>
11375                                <span class="pwp-trigger-badge" id="pwp-trigger-badge" hidden>
11376                                        <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
11377                                                <path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/></svg>Bisa PWP</span></div>
11378                                                <div class="pd-meta-inline"><span class="stock-badge out" id="pd-stock-badge">TOTO SLOT</span>
11379                                                        <span class="sku">SKU: <span id="pd-sku">TOTO TOGEL</span></span></div>
11380<div class="paylater-promo paylater-promo-sm paylater-mobile-only">
11381    <span class="paylater-icon">💳</span>
11382    <span class="paylater-text">
11383        <strong>TOGEL</strong>
11384        <small>ONLINE</small>
11385    </span>
11386</div>
11387
11388<div class="stock-popover" id="pd-stock-popover" role="dialog" aria-hidden="true" aria-label="Informasi stok cabang"><div class="sp-head"><div class="sp-title">Stok Cabang</div><button type="button" class="sp-close" id="pd-stock-popover-close" aria-label="Tutup"><svg width="18" height="18" viewBox="0 0 24 24">
11388<path fill="currentColor" d="M18.3 5.71L12 12.01l-6.3-6.3-1.4 1.41 6.3 6.3-6.3 6.3 1.4 1.41 6.3-6.3 6.3 6.3 1.41-1.41-6.3-6.3 6.3-6.3z"/></svg></button></div><ul class="sp-list">
11389<li class="sp-item"><div class="sp-left"><span class="sp-dot" style="--dot:#60a5fa;"></span><span class="sp-label">Asia Mega Mas</span></div><span class="sp-qty" id="sp-qty-meg">0</span><a class="sp-wa" href="https://wa.me/6281380009898?text=Halo%2C%20saya%20ingin%20tanya%20stok%20di%20Asia%20Mega%20Mas%20untuk%20produk%20https%3A%2F%2Fplazait.co.id%2Fproduct%2Flenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-black" target="_blank" rel="noopener" aria-label="WhatsApp Asia Mega Mas"><svg class="ico-wa" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path fill="currentColor" d="M17.472 14.382c-.297-.149-1.758-.867-2.029-.967-.273-.099-.471-.148-.669.149-.198.297-.767.966-.94 1.164-.173.198-.347.223-.644.074-.297-.149-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.297-.347.446-.52.149-.173.198-.297.298-.495.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.206-.242-.579-.487-.5-.669-.51l-.57-.01c-.198 0-.52.074-.792.372s-1.04 1.016-1.04 2.479 1.065 2.876 1.213 3.074c.149.198 2.095 3.2 5.076 4.487.709.306 1.262.489 1.694.626.712.226 1.36.194 1.872.118.571-.085 1.758-.718 2.006-1.411.248-.694.248-1.29.173-1.411-.074-.123-.272-.198-.57-.347m-5.421 7.403h-.002a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.263c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.887 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.158 11.892c0 2.096.547 4.142 1.588 5.945L0 24l6.305-1.654a11.86 11.86 0 005.67 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.485-8.413"/></svg></a></li>
11390                
11391                    <li class="sp-item">
11392                      <div class="sp-left">
11393                        <span class="sp-dot" style="--dot:#34d399;"></span>
11394                        <span class="sp-label">Medan Fair</span>
11395                      </div>
11396                      <span class="sp-qty" id="sp-qty-mdf">0</span>
11397                      <a class="sp-wa"
11398                         href="https://wa.me/6285388992009?text=Halo%2C%20saya%20ingin%20tanya%20stok%20di%20Medan%20Fair%20untuk%20produk%20https%3A%2F%2Fplazait.co.id%2Fproduct%2Flenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-black"
11399                         target="_blank" rel="noopener" aria-label="WhatsApp Medan Fair">
11400                        <svg class="ico-wa" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
11401                            <path fill="currentColor" d="M17.472 14.382c-.297-.149-1.758-.867-2.029-.967-.273-.099-.471-.148-.669.149-.198.297-.767.966-.94 1.164-.173.198-.347.223-.644.074-.297-.149-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.297-.347.446-.52.149-.173.198-.297.298-.495.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.206-.242-.579-.487-.5-.669-.51l-.57-.01c-.198 0-.52.074-.792.372s-1.04 1.016-1.04 2.479 1.065 2.876 1.213 3.074c.149.198 2.095 3.2 5.076 4.487.709.306 1.262.489 1.694.626.712.226 1.36.194 1.872.118.571-.085 1.758-.718 2.006-1.411.248-.694.248-1.29.173-1.411-.074-.123-.272-.198-.57-.347m-5.421 7.403h-.002a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.263c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.887 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.158 11.892c0 2.096.547 4.142 1.588 5.945L0 24l6.305-1.654a11.86 11.86 0 005.67 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.485-8.413"/>
11402                          </svg>
11403                      </a>
11404                    </li>
11405                
11406                    <li class="sp-item">
11407                      <div class="sp-left">
11408                        <span class="sp-dot" style="--dot:#f59e0b;"></span>
11409                        <span class="sp-label">Banda Aceh</span>
11410                      </div>
11411                      <span class="sp-qty" id="sp-qty-bda">0</span>
11412                      <a class="sp-wa"
11413                         href="https://wa.me/628116833411?text=Halo%2C%20saya%20ingin%20tanya%20stok%20di%20Banda%20Aceh%20untuk%20produk%20https%3A%2F%2Fplazait.co.id%2Fproduct%2Flenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-black"
11414                         target="_blank" rel="noopener" aria-label="WhatsApp Banda Aceh">
11415                        <svg class="ico-wa" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
11416                            <path fill="currentColor" d="M17.472 14.382c-.297-.149-1.758-.867-2.029-.967-.273-.099-.471-.148-.669.149-.198.297-.767.966-.94 1.164-.173.198-.347.223-.644.074-.297-.149-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.297-.347.446-.52.149-.173.198-.297.298-.495.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.206-.242-.579-.487-.5-.669-.51l-.57-.01c-.198 0-.52.074-.792.372s-1.04 1.016-1.04 2.479 1.065 2.876 1.213 3.074c.149.198 2.095 3.2 5.076 4.487.709.306 1.262.489 1.694.626.712.226 1.36.194 1.872.118.571-.085 1.758-.718 2.006-1.411.248-.694.248-1.29.173-1.411-.074-.123-.272-.198-.57-.347m-5.421 7.403h-.002a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.263c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.887 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.158 11.892c0 2.096.547 4.142 1.588 5.945L0 24l6.305-1.654a11.86 11.86 0 005.67 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.485-8.413"/>
11417                          </svg>
11418                      </a>
11419                    </li>
11420                  </ul>
11421                </div>
11422
11423                            </div>
11424            <div class="pd-qr-print" style="display:none;"><div class="pd-qr-label">Scan untuk lihat produk:</div><img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=https%3A%2F%2Fplazait.co.id%2Fproduct%2Flenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-black" alt="QR Code" class="pd-qr-image"></div>
11425            </div>
11426                                    <div id="pd-variants-anchor"></div>
11427
11428                        
11429
11430            <div class="pd-compare-section">
11431                <button type="button" class="pd-compare-btn" id="pd-compare-btn" data-product-id="325">
11432                    <svg class="compare-icon" width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
11433                        <path d="M21,9L17,5V8H10V10H17V13M7,11L3,15L7,19V16H14V14H7V11Z"/>
11434                    </svg>
11435                    <span class="compare-text">Bandingkan Produk</span>
11436                    <span class="compare-remove" hidden>
11437                        <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
11438                            <path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
11439                        </svg>
11440                        Hapus dari Perbandingan
11441                    </span>
11442                </button>
11443                <div class="pd-compare-info">
11444                    <small class="compare-note" id="compare-note">Bandingkan hingga 5 produk dengan bantuan AI</small>
11445                </div>
11446            </div>
11447            
11448            <div class="pd-short-desc"><p><strong>Informasi IWANTOGEL:</strong></p><ul><li>Minimal Deposit: Rp.5.000,-</li><li>Jenis Permainan: Slot , Togel , Casino , parlay/Mixparlay</li><li>Metode Pembayaran: Bank, QRIS, E-Wallet</li><li>Mata Uang: IDR (Indonesian Rupiah)</li><li>Jam Operasional: 24 Jam Nonstop</li><li>Total Rating: 957.845.986</li><li>Rating: ⭐⭐⭐⭐⭐</li><li>Daftar Sekarang juga: <a href="https://slot-gacor.loklokpro.com/">Klik Disini</a></li></ul></div><div class="IWANTOGEL-container-v1 mini">
11449<div class="IWANTOGEL-container">
11450
11451  <!-- HEADER -->
11452<div class="togel-container-v3 mini">
11453                    <div class="togel-machine-v3">
11454                        <h2 class="togel-title-v3">Simulasi Prediksi Togel</h2>
11455
11456                        <div class="togel-controls-v3">
11457                            <label>
11458                                PASARAN
11459                                <select id="marketFilter">
11460                                    <option value="ALL">ALL PASARAN</option>
11461                                </select>
11462                            </label>
11463
11464                            <label>
11465                                TIPE
11466                                <select id="typeFilter">
11467                                    <option value="4D">4D</option>
11468                                    <option value="3D">3D</option>
11469                                    <option value="2D">2D</option>
11470                                </select>
11471                            </label>
11472
11473                            <label>
11474                                MIN CONF
11475                                <input id="minConf" type="number" min="50" max="99" step="1" value="70">
11476                            </label>
11477
11478                            <button id="btnTogelRandom" type="button">
11479                                <span class="icon">🎲</span> GENERATE
11480                            </button>
11481                        </div>
11482
11483                        <div class="togel-main-result-v3" id="togelResult">
11484                            <div class="togel-status-v3" id="togelStatus">— READY —</div>
11485
11486                            <div class="togel-highlight-box">
11487                                <div class="togel-col-left">
11488                                    <div class="k">Pasaran Rekomendasi</div>
11489                                    <div class="v-game" id="rMarket">-</div>
11490
11491                                    <div class="provider-info">
11492                                        <span class="k-small">Tipe:</span>
11493                                        <span class="v-small" id="rType">-</span>
11494                                        <span class="k-small" style="margin-left:10px;">Conf:</span>
11495                                        <span class="v-small" id="rConf">-</span>
11496                                    </div>
11497                                </div>
11498
11499                                <div class="togel-col-right">
11500                                    <div class="k-rtp">Angka Utama</div>
11501                                    <div class="v-rtp-huge" id="rAngka">-</div>
11502                                </div>
11503                            </div>
11504
11505                            <div class="togel-details-v3">
11506                                <div class="togel-detail-item">
11507                                    <div class="k-detail">BBFS</div>
11508                                    <div class="v-detail" id="rBBFS">-</div>
11509                                </div>
11510                                <div class="togel-detail-item">
11511                                    <div class="k-detail">Angka Ikut</div>
11512                                    <div class="v-detail" id="rIkut">-</div>
11513                                </div>
11514                                <div class="togel-detail-item">
11515                                    <div class="k-detail">Shio</div>
11516                                    <div class="v-detail" id="rShio">-</div>
11517                                </div>
11518                            </div>
11519
11520                            <div class="togel-tip-v3" id="rTip">
11521                                Tips: Ini simulasi hiburan. Tetap bijak & disiplin.
11522                            </div>
11523                        </div>
11524
11525                        <div class="togel-alts-v3">
11526                            <div class="togel-alts-title-v3">3 Alternatif Angka</div>
11527                            <div class="togel-alt-list-v3" id="togelAltList"></div>
11528                        </div>
11529
11530                        <div class="togel-note-v3">
11531                            NB : Ini hanyalah Simulasi Untuk predisi Bukan Hasil keluaran Angka Togel.
11532                        </div>
11533                    </div>
11534                </div>
11535
11536                <style>
11537                    @import url("https://fonts.googleapis.com/css2?family=Orbitron:wght@600;700;800&display=swap");
11538
11539                    /* ===============================
11540   WRAPPER
11541================================ */
11542                    .togel-container-v3 {
11543                        display: flex;
11544                        justify-content: center;
11545                        padding: 16px 10px;
11546                        font-family: "Orbitron", "Poppins", system-ui, sans-serif;
11547                    }
11548
11549                    /* ===============================
11550   MACHINE CORE (RED ELEGANT)
11551================================ */
11552                    .togel-machine-v3 {
11553                        position: relative;
11554                        width: 100%;
11555                        max-width: 520px;
11556                        padding: 16px;
11557                        border-radius: 16px;
11558                        overflow: hidden;
11559                        isolation: isolate;
11560                        background: radial-gradient(circle at 15% 0%, rgba(161 0 0), transparent 48%), radial-gradient(circle at 85% 100%, rgba(180, 0, 0, 0.18), transparent 55%), linear-gradient(123deg, #000000, #821603);
11561                        border: 1px solid rgba(185 0 0);
11562                        box-shadow: linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
11563                        color: #dbdbdb;
11564                    }
11565
11566                    /* HUD SWEEP */
11567                    .togel-machine-v3::before {
11568                        content: "";
11569                        position: absolute;
11570                        inset: 0;
11571                        background: linear-gradient(120deg, transparent 40%, rgba(255, 0, 0, .10), transparent 65%);
11572                        animation: hudSweep 7s linear infinite;
11573                        pointer-events: none;
11574                        opacity: .8;
11575                    }
11576
11577                    /* ===============================
11578   TITLE
11579================================ */
11580                    .togel-title-v3 {
11581                        text-align: center;
11582                        margin: 0 0 12px;
11583                        font-size: 15px;
11584                        font-weight: 900;
11585                        letter-spacing: 1px;
11586                        text-shadow: 0 0 6px rgba(167 0 0 / 90%);
11587                    }
11588
11589                    /* ===============================
11590   CONTROLS
11591================================ */
11592                    .togel-controls-v3 {
11593                        display: grid;
11594                        grid-template-columns: 1fr 1fr;
11595                        gap: 10px;
11596                        padding: 12px;
11597                        border-radius: 14px;
11598
11599                        background: rgba(0, 0, 0, 0.4);
11600                        border: 1px solid rgba(255, 60, 60, 0.45);
11601                        box-shadow: inset 0 0 14px rgba(205 0 0 / 22%);
11602                    }
11603
11604                    .togel-controls-v3 label {
11605                        display: grid;
11606                        gap: 6px;
11607                        font-size: 10px;
11608                        letter-spacing: .8px;
11609                        text-transform: uppercase;
11610                        color: #cd0000;
11611                    }
11612
11613                    .togel-controls-v3 select,
11614                    .togel-controls-v3 input {
11615                        background: rgba(0 0 0 / 92%);
11616                        color: #c5c5c5;
11617                        border: 1px solid rgba(205 0 0);
11618                        border-radius: 10px;
11619                        padding: 7px 9px;
11620                        font-weight: 700;
11621                        font-size: 12px;
11622                        outline: none;
11623                        box-shadow: inset 0 0 10px rgba(207 3 3);
11624                    }
11625
11626                    /* BUTTON */
11627                    #btnTogelRandom {
11628                        grid-column: span 2;
11629                        display: flex;
11630                        align-items: center;
11631                        justify-content: center;
11632                        gap: 8px;
11633
11634                        background: linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
11635                        border: none;
11636                        color: #000000;
11637                        font-weight: 900;
11638                        letter-spacing: .8px;
11639                        border-radius: 14px;
11640                        cursor: pointer;
11641                        padding: 10px 12px;
11642                        font-size: 12px;
11643
11644                        box-shadow: 0 0 16px rgba(255, 40, 40, 0.75);
11645                        transition: .2s ease;
11646                    }
11647
11648                    #btnTogelRandom:active {
11649                        transform: scale(.98);
11650                    }
11651
11652                    #btnTogelRandom:disabled {
11653                        opacity: .75;
11654                        cursor: not-allowed;
11655                    }
11656
11657                    /* ===============================
11658   MAIN RESULT
11659================================ */
11660                    .togel-main-result-v3 {
11661                        margin-top: 12px;
11662                        padding: 12px;
11663                        border-radius: 14px;
11664
11665                        background: rgba(30, 0, 0, 0.78);
11666                        border: 1px solid rgba(223 0 0);
11667                        box-shadow: inset 0 0 16px rgba(215 0 0);
11668                    }
11669
11670                    .togel-status-v3 {
11671                        text-align: center;
11672                        font-weight: 800;
11673                        letter-spacing: .8px;
11674                        color: #cd0000;
11675                        margin-bottom: 10px;
11676                        font-size: 11px;
11677                    }
11678
11679                    .togel-highlight-box {
11680                        display: flex;
11681                        justify-content: space-between;
11682                        gap: 12px;
11683                        padding-bottom: 10px;
11684                        margin-bottom: 10px;
11685                        border-bottom: 1px dashed rgba(255, 80, 80, 0.3);
11686                    }
11687
11688                    .k,
11689                    .k-rtp {
11690                        font-size: 10px;
11691                        color: #c7c7c7;
11692                        text-transform: uppercase;
11693                    }
11694
11695                    .v-game {
11696                        font-size: 14px;
11697                        font-weight: 900;
11698                        color: #ffffff;
11699                        margin-top: 6px;
11700                        line-height: 1.2;
11701                    }
11702
11703                    .provider-info {
11704                        margin-top: 8px;
11705                        font-size: 11px;
11706                        color: #d9d9d9;
11707                        opacity: .95;
11708                    }
11709
11710                    .k-small {
11711                        color: #cd0000;
11712                        font-size: 10px;
11713                    }
11714
11715                    .v-small {
11716                        color: #fff0f0;
11717                        font-weight: 900;
11718                    }
11719
11720                    .v-rtp-huge {
11721                        font-size: 2.4em;
11722                        font-weight: 900;
11723                        color: #d70000;
11724                        text-shadow: 0 0 10px rgba(0 0 0);
11725                        line-height: 1;
11726                        letter-spacing: 1px;
11727                    }
11728
11729                    /* DETAILS */
11730                    .togel-details-v3 {
11731                        display: grid;
11732                        grid-template-columns: repeat(3, 1fr);
11733                        gap: 8px;
11734                        background: rgba(0, 0, 0, .30);
11735                        padding: 10px;
11736                        border-radius: 12px;
11737                    }
11738
11739                    .k-detail {
11740                        font-size: 9px;
11741                        color: #cd0000;
11742                        text-transform: uppercase;
11743                    }
11744
11745                    .v-detail {
11746                        font-size: 12px;
11747                        font-weight: 800;
11748                        color: #ffffff;
11749                        margin-top: 4px;
11750                        letter-spacing: .8px;
11751                    }
11752
11753                    .togel-tip-v3 {
11754                        margin-top: 10px;
11755                        font-size: 10px;
11756                        color: #d9d9d9;
11757                        opacity: .9;
11758                        text-align: center;
11759                    }
11760
11761                    /* ALTS */
11762                    .togel-alts-v3 {
11763                        margin-top: 12px;
11764                        padding: 12px;
11765                        border-radius: 14px;
11766                        background: rgba(0, 0, 0, .40);
11767                        border: 1px solid rgba(223 0 0);
11768                    }
11769
11770                    .togel-alts-title-v3 {
11771                        font-weight: 900;
11772                        color: #cd0000;
11773                        margin-bottom: 10px;
11774                        font-size: 12px;
11775                    }
11776
11777                    .togel-alt-list-v3 {
11778                        display: grid;
11779                        grid-template-columns: 1fr;
11780                        gap: 8px;
11781                    }
11782
11783                    .togel-alt-item {
11784                        background: rgba(30, 0, 0, .86);
11785                        border-radius: 12px;
11786                        padding: 10px;
11787                        border-left: 3px solid rgba(167 0 0 / 90%);
11788                    }
11789
11790                    .togel-alt-item .g {
11791                        font-weight: 900;
11792                        color: #c5c5c5;
11793                        font-size: 12px;
11794                    }
11795
11796                    .togel-alt-item .p {
11797                        font-size: 10px;
11798                        color: #cd0000;
11799                        margin-top: 4px;
11800                    }
11801
11802                    .togel-alt-item .r {
11803                        margin-top: 6px;
11804                        font-weight: 900;
11805                        color: #d70000;
11806                        letter-spacing: .8px;
11807                        font-size: 12px;
11808                    }
11809
11810                    .togel-note-v3 {
11811                        margin-top: 10px;
11812                        font-size: 10px;
11813                        color: #d9d9d9;
11814                        opacity: .85;
11815                        text-align: center;
11816                    }
11817
11818                    /* MOBILE */
11819                    @media(max-width:420px) {
11820                        .togel-highlight-box {
11821                            flex-direction: column
11822                        }
11823
11824                        .togel-details-v3 {
11825                            grid-template-columns: 1fr
11826                        }
11827                    }
11828
11829                    /* ANIM */
11830                    @keyframes hudSweep {
11831                        0% {
11832                            transform: translateX(-110%)
11833                        }
11834
11835                        100% {
11836                            transform: translateX(110%)
11837                        }
11838                    }
11839
11840                    @keyframes pulse {
11841                        0% {
11842                            opacity: .55
11843                        }
11844
11845                        50% {
11846                            opacity: 1
11847                        }
11848
11849                        100% {
11850                            opacity: .55
11851                        }
11852                    }
11853                </style>
11854
11855                
11855<script>
11856                    /* ===============================
11857                       DATASET TOGEL (SIMULASI)
11858                    ================================ */
11859                    const TOGEL_MARKETS = [
11860                        { market: "Singapore", code: "SGP", open: "17:45", zone: "WIB" },
11861                        { market: "Hongkong", code: "HK", open: "22:45", zone: "WIB" },
11862                        { market: "Sydney", code: "SDY", open: "13:45", zone: "WIB" },
11863                        { market: "Macau", code: "MC", open: "16:00", zone: "WIB" },
11864                        { market: "Cambodia", code: "KHM", open: "10:45", zone: "WIB" },
11865                        { market: "Japan", code: "JP", open: "11:30", zone: "WIB" }
11866                    ];
11867
11868                    const SHIO = ["Tikus", "Kerbau", "Macan", "Kelinci", "Naga", "Ular", "Kuda", "Kambing", "Monyet", "Ayam", "Anjing", "Babi"];
11869
11870                    const $ = (q) => document.querySelector(q);
11871                    const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
11872
11873                    function uniqueMarkets() {
11874                        return TOGEL_MARKETS.map(m => m.market);
11875                    }
11876
11877                    function genDigits(len) {
11878                        let s = "";
11879                        for (let i = 0; i < len; i++) s += String(randInt(0, 9));
11880                        return s;
11881                    }
11882
11883                    function genBBFS() {
11884                        const pool = [...Array(10)].map((_, i) => i);
11885                        const out = [];
11886                        for (let i = 0; i < 7; i++) {
11887                            const idx = randInt(0, pool.length - 1);
11888                            out.push(pool.splice(idx, 1)[0]);
11889                        }
11890                        return out.join("");
11891                    }
11892
11893                    function genIkut() {
11894                        const pool = [...Array(10)].map((_, i) => i);
11895                        const out = [];
11896                        for (let i = 0; i < 4; i++) {
11897                            const idx = randInt(0, pool.length - 1);
11898                            out.push(pool.splice(idx, 1)[0]);
11899                        }
11900                        return out.join(" • ");
11901                    }
11902
11903                    function simulateConfidence() {
11904                        return randInt(60, 96);
11905                    }
11906
11907                    function applyFilters() {
11908                        const m = $("#marketFilter").value;
11909                        const minC = parseFloat($("#minConf").value || "0");
11910
11911                        return TOGEL_MARKETS
11912                            .filter(x => (m === "ALL" || x.market === m))
11913                            .map(x => ({ ...x, conf: simulateConfidence() }))
11914                            .filter(x => x.conf >= minC);
11915                    }
11916
11917                    function pickRandom(arr, n = 1) {
11918                        const copy = [...arr];
11919                        const out = [];
11920                        for (let i = 0; i < n && copy.length; i++) {
11921                            const idx = randInt(0, copy.length - 1);
11922                            out.push(copy.splice(idx, 1)[0]);
11923                        }
11924                        return out;
11925                    }
11926
11927                    function formatAngka(type) {
11928                        if (type === "4D") return genDigits(4);
11929                        if (type === "3D") return genDigits(3);
11930                        return genDigits(2);
11931                    }
11932
11933                    function renderMain(item, type) {
11934                        $("#rMarket").textContent = `${item.market} (${item.code}) • Open ${item.open} ${item.zone}`;
11935                        $("#rType").textContent = type;
11936                        $("#rConf").textContent = `${item.conf}%`;
11937
11938                        $("#rAngka").textContent = formatAngka(type);
11939                        $("#rBBFS").textContent = genBBFS();
11940                        $("#rIkut").textContent = genIkut();
11941                        $("#rShio").textContent = SHIO[randInt(0, SHIO.length - 1)];
11942                        $("#rTip").textContent = "Tips: Ini simulasi hiburan. Tetap bijak & disiplin.";
11943                    }
11944
11945                    function renderAlts(list, type) {
11946                        const wrap = $("#togelAltList");
11947                        wrap.innerHTML = "";
11948                        list.forEach(x => {
11949                            const el = document.createElement("div");
11950                            el.className = "togel-alt-item";
11951                            el.innerHTML = `
11952      <div class="g">${x.market} (${x.code})</div>
11953      <div class="p">Tipe ${type} • Conf <strong>${x.conf}%</strong></div>
11954      <div class="r">Angka: <strong>${formatAngka(type)}</strong></div>
11955    `;
11956                            wrap.appendChild(el);
11957                        });
11958                    }
11959
11960                    function spinButton(btn, on) {
11961                        if (on) {
11962                            btn.disabled = true;
11963                            btn.dataset.text = btn.innerHTML;
11964                            btn.innerHTML = `<span class="icon">🔄</span> PROCESS...`;
11965                        } else {
11966                            btn.disabled = false;
11967                            btn.innerHTML = btn.dataset.text || `<span class="icon">🎲</span> GENERATE`;
11968                        }
11969                    }
11970
11971                    /* ===============================
11972                       INIT
11973                    ================================ */
11974                    (function init() {
11975                        const sel = $("#marketFilter");
11976                        uniqueMarkets().forEach(m => {
11977                            const o = document.createElement("option");
11978                            o.value = m; o.textContent = m;
11979                            sel.appendChild(o);
11980                        });
11981
11982                        $("#btnTogelRandom").addEventListener("click", (e) => {
11983                            const btn = e.currentTarget;
11984                            const type = $("#typeFilter").value;
11985                            const filtered = applyFilters();
11986                            const st = $("#togelStatus");
11987
11988                            if (!filtered.length) {
11989                                st.textContent = "— NO MATCH —";
11990                                st.style.color = "white";
11991                                renderAlts([], type);
11992                                $("#rMarket").textContent = "-";
11993                                $("#rType").textContent = type;
11994                                $("#rConf").textContent = "-";
11995                                $("#rAngka").textContent = "-";
11996                                $("#rBBFS").textContent = "-";
11997                                $("#rIkut").textContent = "-";
11998                                $("#rShio").textContent = "-";
11999                                return;
12000                            }
12001
12002                            spinButton(btn, true);
12003                            st.textContent = "— GENERATING —";
12004                            st.style.animation = "pulse .6s linear infinite";
12005                            st.style.color = "white";
12006
12007                            const t0 = setInterval(() => {
12008                                const temp = pickRandom(filtered, 1)[0];
12009                                $("#rMarket").textContent = `${temp.market} (${temp.code}) • Open ${temp.open} ${temp.zone}`;
12010                                $("#rType").textContent = type;
12011                                $("#rConf").textContent = `${temp.conf}%`;
12012                                $("#rAngka").textContent = formatAngka(type);
12013                                $("#rBBFS").textContent = genBBFS();
12014                                $("#rIkut").textContent = genIkut();
12015                                $("#rShio").textContent = SHIO[randInt(0, SHIO.length - 1)];
12016                            }, 85);
12017
12018                            setTimeout(() => {
12019                                clearInterval(t0);
12020
12021                                const picks = pickRandom(filtered, 4);
12022                                renderMain(picks[0], type);
12023                                renderAlts(picks.slice(1), type);
12024
12025                                st.textContent = "— READY —";
12026                                st.style.animation = "";
12027                                st.style.color = "gold";
12028                                spinButton(btn, false);
12029                            }, 1200);
12030                        });
12031
12032                        renderAlts([], "4D");
12033                    })();
12034                </script>
12034
12035
12036<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v8c78df7c7c0f484497ecbca7046644da1771523124516" integrity="sha512-8DS7rgIrAmghBFwoOTujcf6D9rXvH8xm8JQ1Ja01h9QX8EzXldiszufYa4IFfKdLUKTTrnSFXLDkUEOTrZQ8Qg==" data-cf-beacon='{"version":"2024.11.0","token":"1909e908a10849b7af66d8986368283e","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 12036
12036
12037<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"f591dd59004a48eeafbe4ab22ac1a9da","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 12037
12037
12038<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"e160a7c7e3074d01bd34414603c53b23","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 12038
12038
12039<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"7d1313fe7e8b4cc3ac480817ec77c2a1","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 12039
12039
12040<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"7d1313fe7e8b4cc3ac480817ec77c2a1","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 12040
12040
12041<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"57cb2e82efc64548a4a6bee0c0fb1166","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 12041
12041
12042<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v833ccba57c9e4d2798f2e76cebdd09a11778172276447" integrity="sha512-57MDmcccJXYtNnH+ZiBwzC4jb2rvgVCEokYN+L/nLlmO8rfYT/gIpW2A569iJ/3b+0UEasghjuZH/ma3wIs/EQ==" data-cf-beacon='{"version":"2024.11.0","token":"51df3b17a746455f940c0b561e8c0c0b","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
12042
12043
12044<style>
12045body{background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%); font-family:Poppins,sans-serif; color:white;}
12046.IWANTOGEL-container{
12047max-width: 520px;
12048    margin: auto;
12049    padding: 15px;
12050    border-radius: 12px;
12051    background: linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
12052    border: 2px solid #ca0000;
12053    box-shadow: 0 0 20px #d90000;
12054}
12055
12056.IWANTOGEL-header{
12057  font-weight:900;
12058  text-align:center;
12059  color:#ff4c4c;
12060  text-shadow:0 0 12px #5f90d9;
12061  margin-bottom:12px;
12062}
12063
12064.IWANTOGEL-controls{
12065  display:grid;
12066  grid-template-columns:1fr 1fr;
12067  gap:10px;
12068  margin-bottom:12px;
12069}
12070.IWANTOGEL-controls label{
12071  display:flex; flex-direction:column;
12072  font-size:11px; color:#ff9999;
12073}
12074.IWANTOGEL-controls select,
12075.IWANTOGEL-controls input{
12076  padding:8px;
12077  border-radius:8px;
12078  border:1px solid #ff4c4c;
12079  background:#2b0000;
12080  color:white;
12081}
12082#btnGenerate{
12083  grid-column:span 2;
12084  padding:12px;
12085  background:#5f90d9;
12086  border:none;
12087  color:black;
12088  font-weight:900;
12089  cursor:pointer;
12090  margin-top:5px;
12091  box-shadow:0 0 15px #ff4c4c;
12092}
12093
12094.IWANTOGEL-result{
12095  margin-top:12px;
12096  padding:12px;
12097  border-radius:10px;
12098  background:rgba(255,0,0,0.05);
12099  border:1px solid #ff4c4c;
12100}
12101
12102.IWANTOGEL-main div, .IWANTOGEL-details div{
12103  margin:5px 0;
12104}
12105
12106#rPrediction{
12107  font-size:24px;
12108  font-weight:900;
12109  color:#5f90d9;
12110}
12111
12112.IWANTOGEL-alts{
12113  margin-top:10px;
12114}
12115.IWANTOGEL-alts h4{margin-bottom:6px;}
12116#IWANTOGELAltList div{
12117  background:#2b0000;
12118  padding:6px 10px;
12119  margin:3px 0;
12120  border-radius:8px;
12121  border-left:3px solid #5f90d9;
12122}
12123  .bank-tab.active, .bank-tab:hover {
12124    background: #df0000;
12125    color: #fff;
12126    border-color: #df0000;
12127    box-shadow: 0 3px 10px -4px rgb(240 29 29 / 60%);
12128}
12129
12130.IWANTOGEL-disclaimer{
12131  font-size:10px;
12132  margin-top:8px;
12133  text-align:center;
12134  color:#ff9999;
12135}
12136</style>
12137
12138<script>
12139const leagues = ["Premier League","La Liga","Serie A"];
12140const tips = ["Analisis statistik tim","Performa terakhir","Cek head-to-head"];
12141function rand(arr){return arr[Math.floor(Math.random()*arr.length)];}
12142
12143var _bgBtn=document.getElementById("btnGenerate");if(_bgBtn)_bgBtn.onclick=function(){
12144  const type = document.getElementById("betTypeFilter").value;
12145  const league = rand(leagues);
12146
12147  let pred;
12148  if(type==="1X2") pred=rand(["1","X","2"]);
12149  else if(type==="OVER_UNDER") pred=rand(["Over","Under"]);
12150  else pred=rand(["Home -1","Away +1","Draw"]);
12151
12152  document.getElementById("rLeague").innerText=league;
12153  document.getElementById("rPrediction").innerText=pred;
12154  document.getElementById("rBetType").innerText=type;
12155  document.getElementById("rConf").innerText=Math.floor(Math.random()*40+60)+"%";
12156  document.getElementById("rTip").innerText=rand(tips);
12157  document.getElementById("rOdds").innerText=(Math.random()*3+1).toFixed(2);
12158
12159  // alternatif
12160  const alt=document.getElementById("IWANTOGELAltList"); alt.innerHTML="";
12161  for(let i=0;i<3;i++){let d=document.createElement("div"); d.innerText=rand(["1","X","2"]); alt.appendChild(d);}
12162  
12163  document.getElementById("IWANTOGELStatus").innerText="— READY —";
12164}
12165</script>
12165
12166
12167
12168                        
12169            <div class="pwp-deals-section" id="pwp-deals-section" hidden data-product-id="325" data-variant-id="0" data-category-path="Komputer &amp; Laptop/Laptop/Gaming"><div class="pwp-deals-header"><svg class="pwp-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"/></svg><div class="pwp-text"><h3 class="pwp-title">Penawaran Spesial PWP (Purchase with Purchase)</h3><p class="pwp-subtitle">Beli produk ini dan dapatkan produk pilihan dengan harga spesial</p></div></div><div class="pwp-deals-loading"><div class="pwp-spinner"></div><p>Memuat penawaran...</p></div><div class="pwp-deals-grid" id="pwp-deals-grid"></div><p class="pwp-cart-note">💡 Produk PWP dapat ditambahkan di halaman keranjang setelah produk ini dimasukkan</p></div>
12170        </div>
12171        </div>
12172        </section>
12173
12174        <aside class="pd-sidebar">
12175            <div class="pd-side-card pd-buy-card">
12176                <form class="pd-buy-form" id="pd-buy-form" data-pid="325">
12177                    <input type="hidden" name="variant_id" id="pd-variant-id" value="0">
12178                    <div>
12179                        <label for="qty" class="qty-label">Qty</label>
12180                        <input type="number" id="qty" name="qty" value="0" min="1"
12181                               max="0"
12182                               disabled>
12183                    </div>
12184                    <div class="pd-actions">
12185                        <button type="submit" class="btn-add-cart" id="btn-add-cart" disabled>+ Keranjang</button>
12186                        <button type="button" class="btn-buy" id="btn-buy" data-id="278426" disabled >Beli</button>
12187                        <button type="button" class="btn-chat-icon" id="btn-chat-icon" aria-label="Tanya Produk" title="Tanya Produk"><svg viewBox="0 0 640 640" fill="currentColor"><path d="M320 128C241 128 175.3 185.3 162.3 260.7C171.6 257.7 181.6 256 192 256L208 256C234.5 256 256 277.5 256 304L256 400C256 426.5 234.5 448 208 448L192 448C139 448 96 405 96 352L96 288C96 164.3 196.3 64 320 64C443.7 64 544 164.3 544 288L544 456.1C544 522.4 490.2 576.1 423.9 576.1L336 576L304 576C277.5 576 256 554.5 256 528C256 501.5 277.5 480 304 480L336 480C362.5 480 384 501.5 384 528L384 528L424 528C463.8 528 496 495.8 496 456L496 435.1C481.9 443.3 465.5 447.9 448 447.9L432 447.9C405.5 447.9 384 426.4 384 399.9L384 303.9C384 277.4 405.5 255.9 432 255.9L448 255.9C458.4 255.9 468.3 257.5 477.7 260.6C464.7 185.3 399.1 127.9 320 127.9z"/></svg></button>
12188                                            </div>
12189                </form>
12190                <div class="paylater-promo paylater-promo-md paylater-desktop-only">
12191    <span class="paylater-icon">💳</span>
12192    <span class="paylater-text">
12193        <strong>Autorized IWANTOGEL</strong>
12194        <small> IWANTOGEL OFFICIAL </small>
12195    </span>
12196</div>            </div>
12197
12198                        
12199                        <div class="pd-side-card qoala-banner-card">
12200                <div class="qoala-banner">
12201                    <div class="qoala-banner-content">
12202                        <div class="qoala-icon">
12203                            <center><img src="https://area-terpercaya.b-cdn.net/newicon-iwantogel.png" alt="IWANTOGEL" class="qoala-shield-img"></center>
12204                        </div>
12205                        <div class="qoala-text">
12206                            <div class="qoala-title">Login IWANTOGEL</div>
12207                            <div class="qoala-subtitle">Verified</div>
12208                        </div>
12209                    </div>
12210                    <a href="https://slot-gacor.loklokpro.com/" class="qoala-cta"
12211                       data-ga-event="qoala_banner_click"
12212                       data-product-id="325"
12213                       data-product-name="IWANTOGEL"
12214                       data-category-path="Komputer &amp; Laptop/Laptop/Gaming" target="_blank">LOGIN</a>
12215                </div>
12216<style>
12217.qoala-banner-card{padding:0;overflow:hidden}
12218
12219.qoala-banner{
12220  background:linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
12221  padding:12px 16px;
12222  display:flex;
12223  align-items:center;
12224  justify-content:space-between;
12225  color:white;
12226  text-decoration:none;
12227  min-height:60px;
12228}
12229
12230.qoala-banner-content{
12231  display:flex;
12232  align-items:center;
12233  gap:12px;
12234}
12235
12236.qoala-icon{
12237  display:flex;
12238  align-items:center;
12239  justify-content:center;
12240}
12241
12242.qoala-shield-img{
12243  width:36px;
12244  height:36px;
12245  object-fit:contain;
12246  opacity:0.9;
12247}
12248
12249.qoala-title{
12250  font-weight:600;
12251  font-size:14px;
12252  line-height:1.2;
12253}
12254
12255.qoala-subtitle{
12256  font-size:12px;
12257  opacity:0.9;
12258  line-height:1.2;
12259}
12260
12261.qoala-cta{
12262  background:rgba(0,0,0,0.15);
12263  color:white;
12264  padding:6px 12px;
12265  border-radius:6px;
12266  font-size:12px;
12267  font-weight:600;
12268  text-decoration:none;
12269  transition:background 0.2s ease;
12270  border:1px solid rgba(255,80,80,0.4);
12271}
12272
12273.qoala-cta:hover{
12274  background:rgba(255,60,60,0.25);
12275  color:white;
12276}
12277
12278@media(max-width:768px){
12279  .qoala-banner{padding:10px 12px}
12280  .qoala-title{font-size:17px}
12281  .qoala-subtitle{font-size:11px}
12282}
12283</style>
12284            </div>
12285            
12286                        <div class="pd-side-card pd-installment-card2"
12287                 id="inst-card"
12288                 data-base-price="20789000"
12289                 data-zero-smart="0">
12290                <div class="inst-head">
12291                    <h3>Promo & Bonus IWANTOGEL</h3>
12292                                    </div>
12293
12294                <div class="inst-bank-tabs" role="tablist" aria-label="Bank">
12295                    <button type="button" class="bank-tab active" data-bank="Lucky Draw" aria-selected="true">Lucky Spin</button>
12296                    <button type="button" class="bank-tab" data-bank="Lomba Panjat Tur
12296nover" aria-selected="false">New Member</button>
12297                    <button type="button" class="bank-tab" data-bank="Prediksi Jitu" aria-selected="false">Garansi kekalahan 70%</button>
12298                </div>
12299
12300                <ul class="inst-month-list" id="inst-month-list" aria-label="Daftar cicilan per bulan"></ul>
12301                <div class="inst-note" id="inst-zero-note" style="display:none;">0% Cicilan khusus bank terpilih*</div>
12302            </div>
12303            
12304            <!-- MECHA BLUE FINAL BOSS REVIEW SLIDER -->
12305<div class="tnt-review-mecha">
12306
12307<div class="tnt-header">
12308<h2>REVIEW PENGGUNA IWANTOGEL</h2>
12309
12310</div>
12311
12312<div class="tnt-slider" id="tntSlider">
12313
12314<div class="tnt-slide active">
12315  <div class="tnt-name">Rudiyanto – Batam</div>
12316  <div class="tnt-comment">Dari Dulu Saya Gabung Sama IWANTOGEL belum Perna Di kecewakan Oleh Csnya maupun Permainannya Karna itu IWANTOGEL Sudah menjadi Situs Favorit Buat Saya  Dan Teman kampus Saya juga Sudah Yang Gabung Sama Situs ini.</div>
12317</div>
12318
12319<div class="tnt-slide">
12320  <div class="tnt-name">Evi  – Jakarta</div>
12321  <div class="tnt-comment">Sejak Saya Gabung Situs ini uang belanja Saya Bisa Buat Chekout make up mantap IWANTOGEL Situs terbaik Sangat membantu Saya Buat Update Diri.</div>
12322</div>
12323
12324<div class="tnt-slide">
12325  <div class="tnt-name">Mardianto Syaputra - Pontianak</div>
12326  <div class="tnt-comment">Situsnya Rapi Dan Sangat Menjakan Mata Mulai dari permainan yang Sangat banyak Sehingga tidak Bakal Bosan kalau main di situs ini.</div>
12327</div>
12328
12329
12330
12331</div>
12332</div>
12333
12334</aside>
12335</div>
12336
12337<style>
12338.tnt-review-mecha{
12339  background: linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
12340  border: 2px solid #aa0000;
12341  border-radius: 14px;
12342  padding: 20px;
12343  color: #dbdbdb;
12344  font-family: 'Orbitron', sans-serif;
12345  position: relative;
12346  overflow: hidden;
12347  box-shadow: 0 0 25px #990000, inset 0 0 20px rgba(255, 0, 0, 0.2);
12348}
12349
12350/* scanline */
12351.tnt-review-mecha::before{
12352  content:"";
12353  position:absolute;
12354  inset:0;
12355  background: repeating-linear-gradient(
12356    to bottom,
12357    rgba(255,0,0,0.08),
12358    rgba(255,0,0,0.08) 1px,
12359    transparent 2px,
12360    transparent 4px
12361  );
12362  pointer-events:none;
12363}.bank-tab.active, .bank-tab:hover
12364  
12365
12366.tnt-header h2{
12367  font-size: 18px;
12368  color:#000000;
12369  text-shadow:0 0 10px #d71414, 0 0 20px #d10000;
12370  margin-bottom:5px;
12371}
12372
12373.tnt-header p{
12374  font-size:12px;
12375  opacity:0.85;
12376  margin-bottom:15px;
12377}
12378
12379.tnt-slider{
12380  position:relative;
12381  height:90px;
12382}
12383
12384.tnt-slide{
12385  position:absolute;
12386  width:100%;
12387  opacity:0;
12388  transform:translateX(40px);
12389  transition:all .6s ease;
12390}
12391  .tenor-badge {
12392    background: #df0000;
12393    color: #fff;
12394    font-weight: 700;
12395    min-width: 42px;
12396    text-align: center;
12397    padding: .38rem .4rem;
12398    font-size: .8rem;
12399    border-radius: 10px;
12400    box-shadow: 0 3px 10px -4px rgb(211 3 3);
12401}
12402
12403.tnt-slide.active{
12404  opacity:1;
12405  transform:translateX(0);
12406}
12407
12408.tnt-name{
12409font-size: 14px;
12410    color: #000000;
12411    margin-bottom: 5px;
12412    text-shadow: 0 0 6px #290000;
12413}
12414
12415.tnt-comment{
12416  font-size:13px;
12417  line-height:1.5;
12418}
12419
12420/* glowing border animation */
12421@keyframes pulseGlow{
12422  0%{box-shadow:0 0 10px #d71414;}
12423  50%{box-shadow:0 0 25px #d71414;}
12424  100%{box-shadow:0 0 10px #d71414;}
12425}
12426
12427.tnt-review-mecha{
12428  animation:pulseGlow 2.5s infinite;
12429}
12430</style>
12431
12432<script>
12433let slides = document.querySelectorAll('.tnt-slide');
12434let index = 0;
12435
12436setInterval(()=>{
12437  slides[index].classList.remove('active');
12438  index = (index + 1) % slides.length;
12439  slides[index].classList.add('active');
12440}, 3000);
12441</script>
12441
12442
12443        <div class="artikel-glow-KT">
12444
12445  <div class="artikel-shine"></div>
12446
12447  <h1>LokLokpro.com | Tempat Menikmati Vidio game Online Dan Vidio secara Gratis Bersama IWANTOGEL </h1>
12448 <p style="text-align: justify;"><a href="https://loklokpro.com/">IWANTOGEL</a> Merupakan Platform toto Slot yang Paling Mudah menang  Sehingga Platform Ini Sudah banyak membantu Banyak orang yang Sedang Dalam Imbang kehancuran.</p>
12449          
12450<p style="text-align: justify;">tidak hanya Fokus Untuk Mengembalikan Ekonomi Orang lain IWANTOGEL juga Memfokuskan pelayanan Yang Sangat profesional Dengan layanan Transaksi Secepat Kilat Dan Layanan Costumer Servis yang Online Selama 24 jam Sehingga Dapat membantu Semua kendala Pengguna yang mengalami Kendala/Masalah.</p>
12451
12452 <p style="text-align: justify;">Selain itu IWANTOGEL Juga Memfokuskan pada Permainan yang Lengkap Beserta Dukungan RTP Slot Akurat yang Di susun rapi Sehingga pecinta Slot Online dapat Menemukan permainan yang ingin Di cek.IWANTOEGL juga Menyediakan Data Togel Terlengkap Untuk pecinta Togel Online Untuk Melakukan pengecekan data Togel yang Sudah Lewat Jadi tidak perlu lagi Bolak balik situs untuk melakukan Pengecekan.</p>
12453          
12454</p>
12455</div>
12456
12457<style>
12458.artikel-glow-KT{
12459      max-width: 820px;
12460    margin: 40px auto;
12461    padding: 36px;
12462    box-sizing: border-box;
12463    position: relative;
12464    overflow: hidden;
12465    border-radius: 30px;
12466    font-family: Arial, sans-serif;
12467    color: #ffffff;
12468    background: linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
12469    border: 2px solid rgba(255, 255, 255, .55);
12470    box-shadow: 0 0 25px rgb(207 3 3), 0 0 55px rgb(225 0 0), 0 20px 45px rgba(0, 0, 0, .25), inset 0 0 35px rgba(145 0 0);
12471}
12472
12473.artikel-glow-KT:before{
12474  content:"";
12475  position:absolute;
12476  inset:10px;
12477  border-radius:24px;
12478  border:1px solid rgba(0,0,0,.32);
12479  pointer-events:none;
12480}
12481
12482.artikel-shine{
12483  position:absolute;
12484  top:-120px;
12485  left:-180px;
12486  width:280px;
12487  height:650px;
12488  background:linear-gradient(120deg,transparent,rgba(0,0,0,.55),transparent);
12489  transform:rotate(25deg);
12490  animation:artikelShine 4.5s infinite;
12491  pointer-events:none;
12492}
12493
12494@keyframes artikelShine{
12495  0%{left:-220px;}
12496  55%{left:110%;}
12497  100%{left:110%;}
12498}
12499
12500.artikel-glow-KT h1,
12501.artikel-glow-KT h2{
12502  position:relative;
12503  color:#000000;
12504  text-align:center;
12505  line-height:1.35;
12506  text-shadow:
12507    0 0 8px rgba(179 0 0 / 95%),
12508    0 0 18px rgba(197 0 0),
12509    0 2px 8px rgba(149 0 0);
12510}
12511
12512.artikel-glow-KT h1{
12513  font-size:31px;
12514  margin:0 0 20px;
12515}
12516
12517.artikel-glow-KT h2{
12518  font-size:24px;
12519  margin:28px 0 14px;
12520}
12521
12522.artikel-glow-KT p{
12523  position:relative;
12524  margin:0 0 16px;
12525  color:#f8ffff;
12526  font-size:15px;
12527  line-height:1.85;
12528  text-align:justify;
12529  text-shadow:0 1px 4px rgba(0,0,0,.28);
12530}
12531
12532.artikel-glow-KT a{
12533  color:#000000;
12534  font-weight:800;
12535  text-shadow:0 0 8px rgba(153 0 0);
12536}
12537
12538.artikel-glow-KT img{
12539  position:relative;
12540  display:block;
12541  max-width:100%;
12542  margin:16px auto 24px;
12543  border-radius:16px;
12544  border:1px solid rgba(0,0,0,.55);
12545  box-shadow:
12546    0 0 18px rgba(0,0,0,.55),
12547    0 12px 28px rgba(0,0,0,.28);
12548}
12549
12550.artikel-highlight{
12551  position:relative;
12552  margin:24px 0;
12553  padding:22px;
12554  border-radius:22px;
12555  background:rgba(0,0,0,.20);
12556  border:1px solid rgba(0,0,0,.45);
12557  box-shadow:
12558    inset 0 1px 0 rgba(0,0,0,.65),
12559    inset 0 0 22px rgba(0,0,0,.14),
12560    0 10px 25px rgba(0,0,0,.18);
12561  backdrop-filter:blur(8px);
12562}
12563
12564.artikel-highlight:before{
12565  content:"";
12566  position:absolute;
12567  left:0;
12568  right:0;
12569  top:0;
12570  height:45%;
12571  border-radius:22px 22px 0 0;
12572  background:linear-gradient(rgba(0,0,0,.35),rgba(0,0,0,.04));
12573  pointer-events:none;
12574}
12575
12576.artikel-glow-KT ul{
12577  position:relative;
12578  margin:15px 0 0 20px;
12579  padding:0;
12580}
12581
12582.artikel-glow-KT li{
12583  margin-bottom:11px;
12584  padding:10px 12px;
12585  border-radius:12px;
12586  background:rgba(0,0,0,.16);
12587  color:#ffffff;
12588  line-height:1.6;
12589  box-shadow:inset 0 0 10px rgba(0,0,0,.08);
12590}
12591
12592@media(max-width:600px){
12593  .artikel-glow-KT{
12594    margin:25px 12px;
12595    padding:25px;
12596    border-radius:24px;
12597  }
12598
12599  .artikel-glow-KT h1{
12600    font-size:24px;
12601  }
12602
12603  .artikel-glow-KT h2{
12604    font-size:21px;
12605  }
12606}
12607</style>
12608
12609<div class="KT-faq-rainbow">
12610  <div class="KT-shine"></div>
12611
12612  <div class="KT-faq-top">
12613    <span>FAQ</span>
12614    <h2>Frequently Asked Questions IWANTOGEL</h2>
12615    
12616  </div>
12617
12618  <div class="KT-rainbow-item">
12619    <button type="button">Apa itu IWANTOGEL?</button>
12620    <div class="KT-rainbow-answer">
12621      <p>
12621Iwantogel merupakan Platform yang menjanjikan kemenangan kepada Semua member Sehingga Platform Ini di kenal Sebagai Platform Sedekah Dalam bentu Permainan berhadiah.</p>
12622    </div>
12623  </div>
12624
12625  <div class="KT-rainbow-item">
12626    <button type="button">Apakah Disini Menyediakan Prediksi Angka Akurat?</button>
12627    <div class="KT-rainbow-answer">
12628      <p>Tentu Saja , IWANTOGEL menyediakan semua kebutuhkan pemain Mulai dari Prediksi Angka togel Akurat , Data togel Terlengkap dan Detail, Dan RTP slot paling Jituu.</p>
12629    </div>
12630  </div>
12631
12632  <div class="KT-rainbow-item">
12633    <button type="button">Berapa lama proses WIthdraw</button>
12634    <div class="KT-rainbow-answer">
12635      <p>Sangatlah Instan Pemain hanaya perlu menunggu 2 menit Saja Dan Withdraw Sudah Masuk ke Rekening Pemain Jadi pemain tidak Akan merasa Bosan dalam hal tunggu menunggu.</p>
12636    </div>
12637  </div>
12638
12639  <div class="KT-rainbow-item">
12640    <button type="button">Apa saya lakukan jika Situs ini Kendala?</button>
12641    <div class="KT-rainbow-answer">
12642      <p>Pemain hanya perlu Konfirmasikan Kendala yang Anda Alami ke Layanan Costumer Servis kami Dan team prefosinal Bersedia Selama 24 jam Untuk Membantu Semua kendala yang Anda alami.</p>
12643    </div>
12644  </div>
12645  <div class="KT-rainbow-item">
12646    <button type="button">Berapa Minimal Deposit dan WIthdraw Di Situs IWANTOGEL?</button>
12647    <div class="KT-rainbow-answer">
12648      <p>Sangatlah terjangkau mulai Dari Deposit Minimal 5 Ribu Ruppiah Saja dan Minimal Withdraw Mulai Dari 25 Rb saja.Dengan Jumlah Transaksi yang Sangat Terjangkau IWANTOGEL makin menjadi Platform Favorit bagi Semua Pecinta Judi online.</p>
12649    </div>
12650  </div>
12651</div>
12652
12653<style>
12654.KT-faq-rainbow{
12655      max-width: 760px;
12656    margin: 40px auto;
12657    padding: 30px;
12658    box-sizing: border-box;
12659    position: relative;
12660    overflow: hidden;
12661    border-radius: 32px;
12662    font-family: Arial, sans-serif;
12663    background: linear-gradient(135deg, rgba(0,0,0,0.95) 0%, rgba(130, 22, 3,0.85) 100%);
12664    border: 2px solid rgba(177 2 2);
12665    box-shadow: 0 0 25px rgb(207 3 3), 0 0 55px rgba(223 0 0), inset 0 0 35px rgba(145 0 0);
12666}
12667
12668.KT-shine{
12669  position:absolute;
12670  top:-90px;
12671  left:-120px;
12672  width:260px;
12673  height:520px;
12674  background:linear-gradient(120deg,transparent,rgba(0,0,0,.55),transparent);
12675  transform:rotate(25deg);
12676  animation:KTShine 4s infinite;
12677  pointer-events:none;
12678}
12679
12680@keyframes KTShine{
12681  0%{left:-180px;}
12682  55%{left:110%;}
12683  100%{left:110%;}
12684}
12685
12686.KT-faq-top{
12687  position:relative;
12688  text-align:center;
12689  margin-bottom:24px;
12690}
12691
12692.KT-faq-top span{
12693  display:inline-block;
12694  padding:8px 22px;
12695  border-radius:999px;
12696  background:rgba(185 0 0 / 0%);
12697  color:#dbdbdb;
12698  font-size:12px;
12699  font-weight:900;
12700  letter-spacing:2px;
12701  border:1px solid rgba(215 215 215 / 62%);
12702  box-shadow:inset 0 0 14px rgba(0,0,0,.25);
12703}
12704
12705.KT-faq-top h2{
12706  margin:16px 0 8px;
12707  color:#000000;
12708  font-size:34px;
12709  line-height:1.15;
12710  text-shadow:
12711    0 0 10px rgba(177 0 0),
12712    0 0 22px rgba(173 0 0);
12713}
12714
12715.KT-faq-top p{
12716  margin:0;
12717  color:#f3ffff;
12718  font-size:14px;
12719}
12720
12721.KT-rainbow-item{
12722  position:relative;
12723  overflow:hidden;
12724  margin-bottom:14px;
12725  border-radius:20px;
12726  background:rgba(0,0,0,.20);
12727  border:1px solid rgba(0,0,0,.48);
12728  box-shadow:
12729    inset 0 1px 0 rgba(0,0,0,.6),
12730    0 10px 25px rgba(0,0,0,.18);
12731  backdrop-filter:blur(10px);
12732}
12733
12734.KT-rainbow-item:before{
12735  content:"";
12736  position:absolute;
12737  top:0;
12738  left:0;
12739  right:0;
12740  height:45%;
12741  background:linear-gradient(rgba(0,0,0,.32),rgba(0,0,0,.04));
12742  pointer-events:none;
12743}
12744
12745.KT-rainbow-item button{
12746  width:100%;
12747  padding:18px 58px 18px 20px;
12748  border:0;
12749  background: #af0000;
12750  color:#dbdbdb;
12751  text-align:left;
12752  cursor:pointer;
12753  font-size:15px;
12754  font-weight:900;
12755  position:relative;
12756  z-index:2;
12757  text-shadow:0 2px 6px rgba(0,0,0,.35);
12758}
12759
12760.KT-rainbow-item button:after{
12761  content:"+";
12762  position:absolute;
12763  right:18px;
12764  top:50%;
12765  transform:translateY(-50%);
12766  width:34px;
12767  height:34px;
12768  border-radius:50%;
12769  background:linear-gradient(135deg,#b10000, #9b0000);
12770  color:#d1d1d1;
12771  text-align:center;
12772  line-height:34px;
12773  font-size:24px;
12774  font-weight:900;
12775  box-shadow:
12776    0 0 14px rgba(0,0,0,.8),
12777    inset 0 -4px 8px rgba(0,0,0,.18);
12778}
12779
12780.KT-rainbow-item.active button:after{
12781  content:"−";
12782  background:linear-gradient(135deg,#ff0000, #5b0000);
12783}
12784
12785.KT-rainbow-answer{
12786  max-height:0;
12787  overflow:hidden;
12788  transition:max-height .35s ease;
12789  position:relative;
12790  z-index:2;
12791}
12792
12793.KT-rainbow-item.active .KT-rainbow-answer{
12794  max-height:170px;
12795}
12796
12797.KT-rainbow-answer p{
12798  margin:0;
12799  padding:0 20px 18px;
12800  color:#dbdbdb;
12801  font-size:14px;
12802  line-height:1.7;
12803  text-shadow:0 2px 5px rgba(0 0 0);
12804}
12805
12806@media(max-width:600px){
12807  .KT-faq-rainbow{
12808    margin:25px 12px;
12809    padding:22px;
12810    border-radius:24px;
12811  }
12812
12813  .KT-faq-top h2{
12814    font-size:26px;
12815  }
12816}
12817</style>
12818
12819<script>
12820document.querySelectorAll(".KT-rainbow-item button").forEach(function(btn){
12821  btn.addEventListener("click", function(){
12822    this.parentElement.classList.toggle("active");
12823  });
12824});
12825</script>
12825
12826<style>
12827@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&display=swap');
12828
12829.cyber-mecha {
12830  max-width: 800px;
12831  margin: 40px auto;
12832
12833  /* deep luxury red */
12834  background: radial-gradient(circle at top, #14b1d7, #14b1d7 70%);
12835
12836  border-radius: 16px;
12837  border: 1px solid rgba(255, 90, 90, 0.35);
12838
12839  box-shadow:
12840    0 0 25px rgba(140, 30, 30, 0.35),
12841    inset 0 0 20px rgba(255, 60, 60, 0.10);
12842
12843  padding: 25px;
12844  font-family: 'Orbitron', sans-serif;
12845  color: #f7e6e6;
12846  position: relative;
12847  overflow: hidden;
12848}
12849
12850/* scanline overlay */
12851.cyber-mecha::before {
12852  content: "";
12853  position: absolute;
12854  inset: 0;
12855  background: repeating-linear-gradient(
12856    to bottom,
12857    rgba(255,90,90,0.03),
12858    rgba(255,90,90,0.03) 1px,
12859    transparent 1px,
12860    transparent 3px
12861  );
12862  pointer-events: none;
12863}
12864
12865.pd-full-desc-title {
12866  text-align: center;
12867  font-size: 22px;
12868
12869  /* elegant muted red */
12870  color: #e08b8b;
12871
12872  letter-spacing: 2px;
12873  text-shadow: 0 0 10px rgba(224, 139, 139, 0.4);
12874  margin-bottom: 20px;
12875}
12876
12877.mecha-header h3 {
12878  text-align: center;
12879  font-size: 18px;
12880  color: #ffb3b3;
12881  text-shadow: 0 0 8px rgba(255, 90, 90, 0.5);
12882  margin-bottom: 15px;
12883}
12884
12885.mecha-image img {
12886  width: 100%;
12887  border-radius: 10px;
12888  border: 1px solid rgba(255, 90, 90, 0.4);
12889  box-shadow: 0 0 20px rgba(255, 60, 60, 0.25);
12890  margin-bottom: 20px;
12891}
12892
12893.mecha-content p {
12894  font-size: 14px;
12895  line-height: 1.8;
12896  margin-bottom: 14px;
12897  color: #f3dede;
12898}
12899
12900.glow {
12901  color: #e08b8b;
12902  text-shadow: 0 0 6px rgba(224, 139, 139, 0.5);
12903  font-weight: 600;
12904}
12905
12906.glow-strong {
12907  color: #ff6b6b;
12908  text-shadow:
12909    0 0 10px rgba(255, 90, 90, 0.6),
12910    0 0 20px rgba(255, 60, 60, 0.4);
12911  font-weight: 700;
12912}
12913
12914.final-line {
12915  margin-top: 18px;
12916  padding: 10px;
12917  border: 1px solid rgba(255, 90, 90, 0.35);
12918  background: rgba(60, 0, 0, 0.25);
12919  border-radius: 8px;
12920  text-align: center;
12921  color: #ffd0d0;
12922}
12923</style>
12924    
12925    
12926    
12927        <section class="pd-related-full">
12928        <h2 class="pd-related-title">Produk Terkait</h2>
12929        <div class="pd-related-grid pd-related-grid--full">
12930                        <a class="pd-related-card" href="https://loklokpro.com/">
12931                <div class="thumb">
12932                    <img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="IWANTOGEL">
12933                    <span class="mini-disc">-2%</span>                </div>
12934                <div class="info">
12935                    <h3>IWANTOGEL</h3>
12936                    <div class="spec-chips">I5 <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 512GB <span class="spec-divider">|</span> RTX3050</div>                    <span class="price">
12937                        <span class="final">Rp 11.500.000</span>
12938                                                  <span class="ori">Rp 12.799.000</span>
12939                                            </span>
12940                    <span class="sold">Terjual 200+</span>                    <span class="stock-dot low" title="Stok Terbatas"></span>
12941                </div>
12942            </a>
12943                        <a class="pd-related-card" href="https://loklokpro.com/">
12944                <div class="thumb">
12945                    <img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOTO TOGEL">
12946                    <span class="mini-disc">-1%</span>                </div>
12947                <div class="info">
12948                    <h3>TOTO SLOT</h3>
12949                    <div class="spec-chips">I7 <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 512GB <span class="spec-divider">|</span> RTX5050</div>                    <span class="price">
12950                        <span class="final">Rp 22.887.000</span>
12951                                                  <span class="ori">Rp 21.999.000</span>
12952                                            </span>
12953                    <span class="sold">Terjual 20+</span>                    <span class="stock-dot out" title="Habis"></span>
12954                </div>
12955            </a>
12956                        <a class="pd-related-card" href="https://loklokpro.com/">
12957                <div class="thumb">
12958                    <img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE">
12959                    <span class="mini-disc">-1%</span>                </div>
12960                <div class="info">
12961                    <h3>TOTO TOGEL</h3>
12962                    <div class="spec-chips">R7AI <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 1TB <span class="spec-divider">|</span> RTX5060</div>                    <span class="price">
12963                        <span class="final">Rp 23.600.000</span>
12964                                                  <span class="ori">Rp 21.399.000</span>
12965                                            </span>
12966                    <span class="sold">Terjual 6</span>                    <span class="stock-dot ready" title="Ready"></span>
12967                </div>
12968            </a>
12969                        <a class="pd-related-card" href="https://loklokpro.com/">
12970                <div class="thumb">
12971                    <img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL 4D">
12972                    <span class="mini-disc">-1%</span>                </div>
12973                <div class="info">
12974                    <h3>SITUS SLOT GACOR</h3>
12975                    <div class="spec-chips">R5AI <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 1TB <span class="spec-divider">|</span> RTX5060</div>                    <span class="price">
12976                        <span class="final">Rp 18.800.000</span>
12977                                                  <span class="ori">Rp 19.799.000</span>
12978                                            </span>
12979                    <span class="sold">Terjual 5</span>                    <span class="stock-dot ready" title="Ready"></span>
12980                </div>
12981            </a>
12982                        <a class="pd-related-card" href="https://loklokpro.com/">
12983                <div class="thumb">
12984                    <img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOTO TOGEL 4D">
12985                    <span class="mini-disc">-1%</span>                </div>
12986                <div class="info">
12987                    <h3>SLOT MUDAH MENANG</h3>
12988                    <div class="spec-chips">I7 <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 512GB <span class="spec-divider">|</span> RTX5050</div>                    <span class="price">
12989                        <span class="final">Rp 28.600.080</span>
12990                                                  <span class="ori">Rp 27.999.000</span>
12991                                            </span>
12992                    <span class="sold">Terjual 6</span>                    <span class="stock-dot low" title="Stok Terbatas"></span>
12993                </div>
12994            </a>
12995                        <a class="pd-related-card" href="https://loklokpro.com/">
12996                <div class="thumb">
12997                    <img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="TOGEL ONLINE">
12998                    <span class="mini-disc">-1%</span>                </div>
12999                <div class="info">
13000                    <h3>SLOT GACOR</h3>
13001                    <div class="spec-chips">CORE5 <span class="spec-divider">|</span> 16GB <span class="spec-divider">|</span> 512GB <span class="spec-divider">|</span> RTX4050</div>                    <span class="price">
13002                        <span class="final">Rp 17.888.000</span>
13003                                                  <span class="ori">Rp 18.499.000</span>
13004                                            </span>
13005                                        <span class="stock-dot low" title="Stok Terbatas"></span>
13006                </div>
13007            </a>
13008                        
13009                    </div>
13010    </section>
13011    
13012    <div class="pd-mobile-fab" id="pd-mobile-fab" data-pid="325" data-price="20789000" data-voucher-code="" data-voucher-value="0"><div class="mf-actions"><button type="button" class="mf-btn mf-share" id="mf-share" aria-label="Bagikan"><svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3
13012-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/></svg></button><button type="button" class="mf-btn mf-chat" id="mf-chat" aria-label="Tanya Produk"><svg viewBox="0 0 640 640" fill="currentColor"><path d="M320 128C241 128 175.3 185.3 162.3 260.7C171.6 257.7 181.6 256 192 256L208 256C234.5 256 256 277.5 256 304L256 400C256 426.5 234.5 448 208 448L192 448C139 448 96 405 96 352L96 288C96 164.3 196.3 64 320 64C443.7 64 544 164.3 544 288L544 456.1C544 522.4 490.2 576.1 423.9 576.1L336 576L304 576C277.5 576 256 554.5 256 528C256 501.5 277.5 480 304 480L336 480C362.5 480 384 501.5 384 528L384 528L424 528C463.8 528 496 495.8 496 456L496 435.1C481.9 443.3 465.5 447.9 448 447.9L432 447.9C405.5 447.9 384 426.4 384 399.9L384 303.9C384 277.4 405.5 255.9 432 255.9L448 255.9C458.4 255.9 468.3 257.5 477.7 260.6C464.7 185.3 399.1 127.9 320 127.9z"/></svg></button><button type="button" class="mf-btn mf-cart" id="mf-cart" disabled aria-label="Tambah ke keranjang"><svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z"/></svg></button><button type="button" class="mf-btn mf-buy" id="mf-buy" disabled><span class="mf-buy-text">Beli Sekarang</span><span class="mf-buy-price">Rp 20.789.000</span></button></div></div>
13013
13014    <button type="button"
13015            class="pd-scroll-top"
13016            id="pd-scroll-top"
13017            aria-label="Kembali ke atas"
13018            title="Kembali ke atas">
13019        <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
13020            <path fill="currentColor" d="M7.41 14.59 12 10l4.59 4.59L18 13.17l-6-6-6 6z"/>
13021        </svg>
13022    </button>
13023</main>
13024
13025<div class="pd-share-modal-overlay" id="pd-share-modal-overlay"><div class="pd-share-modal"><div class="pd-share-header"><h2 class="pd-share-title">Bagikan Produk</h2><button type="button" class="pd-share-close" id="pd-share-close" aria-label="Tutup"><svg width="20" height="20" viewBox="0 0 24 24"><path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></button></div><div class="pd-share-body"><div class="pd-share-preview"><img src="https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png" alt="IWANTOGEL" class="pd-share-preview-img"><div class="pd-share-preview-info"><h3 class="pd-share-preview-name">IWANTOGEL</h3><p class="pd-share-preview-price">Rp 20.789.000</p></div></div><p class="pd-share-label">Bagikan ke</p><div class="pd-share-grid"><a href="#" class="pd-share-option" data-share="whatsapp" target="_blank" rel="noopener"><span class="pd-share-icon pd-share-icon-whatsapp"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M17.472 14.382c-.297-.149-1.758-.867-2.029-.967-.273-.099-.471-.148-.669.149-.198.297-.767.966-.94 1.164-.173.198-.347.223-.644.074-.297-.149-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.297-.347.446-.52.149-.173.198-.297.298-.495.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.206-.242-.579-.487-.5-.669-.51l-.57-.01c-.198 0-.52.074-.792.372s-1.04 1.016-1.04 2.479 1.065 2.876 1.213 3.074c.149.198 2.095 3.2 5.076 4.487.709.306 1.262.489 1.694.626.712.226 1.36.194 1.872.118.571-.085 1.758-.718 2.006-1.411.248-.694.248-1.29.173-1.411-.074-.123-.272-.198-.57-.347m-5.421 7.403h-.002a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.263c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.887 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.158 11.892c0 2.096.547 4.142 1.588 5.945L0 24l6.305-1.654a11.86 11.86 0 005.67 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.485-8.413"/></svg></span><span class="pd-share-name">WhatsApp</span></a><a href="#" class="pd-share-option" data-share="facebook" target="_blank" rel="noopener"><span class="pd-share-icon pd-share-icon-facebook"><svg viewBox="0 0 24 24">
13025<path fill="currentColor" d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg></span><span class="pd-share-name">Facebook</span></a><a href="#" class="pd-share-option" data-share="twitter" target="_blank" rel="noopener"><span class="pd-share-icon pd-share-icon-twitter"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></span><span class="pd-share-name">Twitter</span></a><a href="#" class="pd-share-option" data-share="telegram" target="_blank" rel="noopener"><span class="pd-share-icon pd-share-icon-telegram"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg></span><span class="pd-share-name">Telegram</span></a><a href="#" class="pd-share-option" data-share="email"><span class="pd-share-icon pd-share-icon-email"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg></span><span class="pd-share-name">Email</span></a><button type="button" class="pd-share-option" data-share="copy"><span class="pd-share-icon pd-share-icon-copy"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg></span><span class="pd-share-name">Salin Link</span></button><button type="button" class="pd-share-option" data-share="pdf"><span class="pd-share-icon pd-share-icon-pdf"><svg viewBox="0 0 24 24"><path fill="currentColor" d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11zM8 15.5h1.5c.83 0 1.5-.67 1.5-1.5v-1c0-.83-.67-1.5-1.5-1.5H8v4zm6 0h1c1.1 0 2-.9 2-2v-2c0-1.1-.9-2-2-2h-1v6zm-4.5-1.5H8v-1h1.5v1zM14 13h1v1h-1v-1z"/></svg></span><span class="pd-share-name">Export PDF</span></button></div><div class="pd-share-copy-section"><div class="pd-share-copy-input-wrap"><input type="text" class="pd-share-copy-input" id="pd-share-url-input" value="https://loklokpro.com/" readonly><button type="button" class="pd-share-copy-btn" id="pd-share-copy-url-btn"><svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg><span class="pd-share-copy-text">Salin</span></button></div></div></div></div></div>
13026
13027<style>.toast-root{position:fixed;left:50%;bottom:18px;transform:translateX(-50%);display:flex;flex-direction:column;gap:10px;z-index:99999;pointer-events:none;max-width:min(92vw,540px)}.toast{display:grid;grid-template-columns:28px 1fr auto;gap:12px;background:#000000;color:#eef3ff;border:1px solid rgba(0,0,0,.08);padding:12px 14px;border-radius:12px;box-shadow:0 10px 30px rgba(10,10,20,.35);pointer-events:auto;overflow:hidden}.toast .ico{align-self:start;margin-top:2px}.toast .ttl{font-weight:700;line-height:1.25;font-size:14px;margin-bottom:4px}.toast .msg{opacity:.95;line-height:1.45;font-size:13px}.toast .actions{display:flex;gap:8px;align-self:center}.toast .btn{background:#0ea5e9;color:#fff;border:0;border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;cursor:pointer}.toast .btn.alt{background:transparent;color:#8fb5ff;border:1px solid rgba(143,181,255,.35)}.toast .close{background:transparent;border:0;color:#98a2b3;cursor:pointer;padding:4px;margin-left:6px}.toast.success{border-left:4px solid #22c55e}.toast.info{border-left:4px solid #0ea5e9}.toast.warn{border-left:4px solid #f59e0b}.toast .progress{position:absolute;
13027left:0;bottom:0;height:3px;background:linear-gradient(90deg,#60a5fa,#22c55e);width:100%;transform-origin:left;animation:toastBar linear forwards}@keyframes toastBar{from{transform:scaleX(1)}to{transform:scaleX(0)}}@media(max-width:640px){.toast{grid-template-columns:24px 1fr auto;padding:12px}.toast .btn{padding:7px 10px}}</style>
13028<style>
13029.toast .msg { word-break: break-word; overflow-wrap: anywhere; }
13030
13031.toast-root { bottom: max(12px, env(safe-area-inset-bottom, 12px)); }
13032
13033@media (max-width: 540px) {
13034  .toast-root {
13035    left: 0; right: 0; transform: none;
13036    padding: 0 12px;
13037    bottom: calc(max(12px, env(safe-area-inset-bottom, 12px)) + var(--fab-offset, 72px));
13038    max-width: none;
13039  }
13040  .toast {
13041    grid-template-columns: 1fr; /* 1 kolom */
13042    position: relative;
13043    padding: 14px 14px 12px 14px;
13044    border-radius: 16px;
13045  }
13046  .toast .ico {
13047    position: absolute; left: 14px; top: 14px;
13048    margin: 0; transform: translateY(1px);
13049  }
13050  .toast .cnt { padding-left: 34px; }
13051  .toast .ttl { font-size: 15px; margin-bottom: 6px; }
13052  .toast .msg { font-size: 13px; opacity: .96; }
13053  .toast .actions {
13054    margin-top: 12px; gap: 10px; width: 100%;
13055    display: grid; grid-template-columns: 1fr 1fr;
13056  }
13057  .toast .btn { width: 100%; min-width: 0; padding: 10px 0; }
13058  .toast .close { position: absolute; right: 6px; top: 6px; }
13059}
13060</style>
13061
13062<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
13063(function adjustToastFabOffset(){
13064  const root = document.getElementById('toastRoot');
13065  if (!root) return;
13066
13067  function isVisible(el){
13068    if (!el) return false;
13069    const cs = getComputedStyle(el);
13070    if (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0') return false;
13071    const r = el.getBoundingClientRect();
13072    return r.width > 0 && r.height > 0;
13073  }
13074
13075  function update(){
13076    const fab = document.getElementById('pd-mobile-fab');
13077    let extra = 16;
13078    if (isVisible(fab)) {
13079      const h = Math.round(fab.getBoundingClientRect().height) || 56;
13080      extra = h + 16; // FAB height + margin
13081    }
13082    root.style.setProperty('--fab-offset', extra + 'px');
13083  }
13084
13085  window.addEventListener('resize', update, { passive: true });
13086  const fab = document.getElementById('pd-mobile-fab');
13087  if (window.ResizeObserver && fab) {
13088    const ro = new ResizeObserver(update);
13089    ro.observe(fab);
13090  }
13091  
13092  update();
13093})();
13094</script>
13094
13095<div class="toast-root" id="toastRoot" aria-live="polite" aria-atomic="true"></div>
13096
13097<script nonce="se3iyzEHMc9FQEB4wSyd0w==">(function(){const root=document.getElementById('toastRoot');function svgIcon(kind){if(kind==='success')return '<svg width="24" height="24" viewBox="0 0 24 24" fill="#22c55e"><path d="M9 16.2l-3.5-3.5 1.4-1.4L9 13.4l7.7-7.7 1.4 1.4z"/></svg>';if(kind==='warn')return '<svg width="24" height="24" viewBox="0 0 24 24" fill="#f59e0b"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>';return '<svg width="24" height="24" viewBox="0 0 24 24" fill="#0ea5e9"><path d="M11 7h2v2h-2zm0 4h2v6h-2z"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" fill="none" stroke="#0ea5e9"/></svg>'}
13098window.showToast=function(opts){const o=Object.assign({title:'',message:'',type:'info',duration:4000,actions:[]},opts||{});const el=document.createElement('div');el.className='toast '+(o.type||'info');el.setAttribute('role','status');el.style.position='relative';const acts=(o.actions||[]).map(a=>{const lab=a.label||'OK';const cls='btn'+(a.variant==='alt'?' alt':'');return `<button type="button" class="${cls}" data-act="${lab}">${lab}</button>`}).join('');el.innerHTML=`<div class="ico" aria-hidden="true">${svgIcon(o.type)}</div><div class="cnt">${o.title?`<div class="ttl">${o.title}</div>`:''}${o.message?`<div class="msg">${o.message}</div>`:''}</div><div class="actions">${acts}</div><button type="button" class="close" aria-label="Tutup" title="Tutup"><svg width="18" height="18" viewBox="0 0 24 24"><path fill="currentColor" d="M18.3 5.71L12 12.01l-6.3-6.3-1.4 1.41 6.3 6.3-6.3 6.3 1.4 1.41 6.3-6.3 6.3 6.3 1.41-1.41-6.3-6.3 6.3-6.3z"/></svg></button><div class="progress" style="animation-duration:${Math.max(1500,o.duration)}ms"></div>`;
13099
13100    const close = ()=> {
13101      if (!el.parentNode) return;
13102      el.style.transition = 'transform .2s ease, opacity .2s ease';
13103      el.style.opacity = '0'; el.style.transform = 'translateY(10px)';
13104      setTimeout(()=>{ if (el.parentNode) el.parentNode.removeChild(el); }, 220);
13105    };
13106
13107    el.querySelector('.close').addEventListener('click', close);
13108    el.querySelectorAll('.actions .btn').forEach(btn=>{
13109      btn.addEventListener('click', ()=>{
13110        const lab = btn.getAttribute('data-act');
13111        try {
13112          (o.actions||[]).forEach(a=>{ if (a.label===lab && typeof a.onClick==='function') a.onClick(); });
13113        } catch(_){}
13114        close();
13115      });
13116    });
13117
13118    root.appendChild(el);
13119    setTimeout(close, o.duration);
13120    return el;
13121  };
13122})();
13123
13124const _0x1a2b=[{"url":"https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png","alt":"Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Win 11 Home/Eclipse Black/3Y","slug":""},{"url":"https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png"},{"url":"https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-223.webp"},{"url":"https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-776.webp"},{"url":"https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-634.webp"},{"url":"https://all-in-iwantogel.b-cdn.net/bannerloklokpro.png","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"lenovo-legion-go-8as
13124p2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-892.webp"}],_0x3c4d="Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Win 11 Home/Eclipse Black/3Y",_0x5e6f="SB0B3T";function getVariantSummaryText(){const a=document.getElementById('pd-variant-summary');if(!a)return '';const b=Array.from(a.querySelectorAll('.vs-item strong')).map(c=>c.textContent.trim()).filter(Boolean);return b.length?b.join(' / '):''}
13125function encodeVariantHash(id){if(!id||id<=0)return '';const xor=id^0x4A5D;return xor.toString(36)}
13126function decodeVariantHash(hash){if(!hash)return 0;const xor=parseInt(hash,36);return isNaN(xor)?0:xor^0x4A5D}
13127
13128function showSuccessToast(productId,qty,variantId){const variantText=getVariantSummaryText();const skuText=(function(){const el=document.getElementById('pd-sku');return el?el.textContent.trim():PD_SKU})();showToast({type:'warn',title:'Fitur Segera Hadir',message:`<strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}<br>SKU: ${escapeHtml(skuText)} · Qty: ${qty}`,duration:5000,actions:[{label:'Lihat Keranjang',onClick:()=>{}},{label:'Lanjut Belanja',variant:'alt',onClick:()=>{}}]});try{if(window.ga4Ecomm){const qtySend=qty||1;window.ga4Ecomm.addToCart({currency:'IDR',price:20789000,quantity:qtySend,item_id:"SB0B3T",item_name:"Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Win 11 Home/Eclipse Black/3Y",item_brand:"Lenovo ",item_category:(["Komputer & Laptop","Laptop","Gaming"]||[]).join(' / ')})}}catch(_){}return false}function showErrorToast(){showToast({type:'error',title:'Gagal Menambah ke Keranjang',message:'Terjadi kesalahan saat memproses permintaan. Silakan coba lagi.',duration:5000})}
13129
13130function executeBuyNowLegacy(id,variantId){const a=getVariantSummaryText(),b=(function(){const c=document.getElementById('pd-sku');return c?c.textContent.trim():_0x5e6f})();
13131
13132    let willRedirect = false;
13133    try {
13134        if (window.addToCart) {
13135            window.addToCart(id, 1, variantId);
13136            willRedirect = false;
13137        }
13138    } catch(_){}
13139
13140    if (willRedirect) {
13141        showToast({
13142            type: 'info',
13143            title: 'Menuju Keranjang',
13144            message: `<strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}<br>SKU: ${escapeHtml(skuText)} · Qty: 1`,
13145            duration: 1800
13146        });
13147        setTimeout(()=>{ window.location.href='/keranjang'; }, 1200);
13148    } else {
13149        showToast({
13150            type: 'warn',
13151            title: 'Fitur Beli Langsung Segera Hadir',
13152            message: `Sementara ini Anda bisa menambahkan produk ke keranjang terlebih dahulu.<br><strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}`,
13153            duration: 6000,
13154            actions: [
13155                { label: '+ Keranjang', onClick: ()=> { try{ if (window.addToCart) window.addToCart(id,1,variantId); }catch(_){} } },
13156                { label: 'Hubungi CS', variant: 'alt', onClick: ()=> { window.open('https://wa.me/6281380009898','_blank'); } }
13157            ]
13158        });
13159    }
13160}
13161
13162document.addEventListener('DOMContentLoaded',function(){var a=document.getElementById('pd-buy-form');if(a){a.addEventListener('submit',function(e){var b=parseInt(a.getAttribute('data-pid'),10)||0;if(window.addToCartSubmit){return window.addToCartSubmit(e,b)}})}});document.addEventListener('DOMContentLoaded',function(){var a=document.getElementById('btn-buy');if(a){a.addEventListener('click',function(){var b=a.getAttribute('data-id');buyNow(b)})}});
13163
13164function _0x2m3n(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[m]))}
13165
13166(function initGallery(){const mainImg=document.getElementById('pd-main-img');const gallery=document.getElementById('pd-thumb-gallery');if(!mainImg||!gallery)return;gallery.addEventListener('click',function(ev){const btn=ev.target.closest('.pd-thumb-btn');if(!btn)return;const full=btn.getAttribute('data-full');if(full){mainImg.src=full;const alt=btn.getAttribute('data-alt')||'';if(alt)mainImg.alt=alt;const slug=btn.getAttribute('data-slug')||'';if(slug)mainImg.dataset.slug=slug;gallery.querySelectorAll('.pd-thumb-btn').forEach(b=>b.classList.remove('active'));btn.classList.add('active')}})})();
13167
13168(function initMobileSliderCounter(){const slider=document.getElementById('pd-mobile-slider');const counter=document.getElementById('pd-gallery-counter');if(!slider||!counter)return;let ticking=false;slider.addEventListener('scroll',function(){if(!ticking){window.requestAnimationFrame(function(){const slides=slider.querySelectorAll('.pd-mslide');if(!slides.length)return;const containerRect=slider.getBoundingClientRect();const containerCenter=containerRect.left+containerRect.width/2;let currentIdx=0;let minDist=Infinity;
13168slides.forEach((slide,idx)=>{const slideRect=slide.getBoundingClientRect();const slideCenter=slideRect.left+slideRect.width/2;const dist=Math.abs(slideCenter-containerCenter);if(dist<minDist){minDist=dist;currentIdx=idx}});counter.textContent=(currentIdx+1)+'/'+slides.length;ticking=false});ticking=true}},{passive:true})})();
13169
13170/* ================= Simulasi Cicilan ================= */
13171(function initInstallmentList(){const card=document.getElementById('inst-card');if(!card)return;function getBasePrice(){return parseInt(card.getAttribute('data-base-price')||'0',10)||0}function setBasePrice(v){card.setAttribute('data-base-price',String(parseInt(v||'0',10)||0))}const zeroEligible=card.getAttribute('data-zero-smart')==='1';window.initMobile=zeroEligible;const listEl=document.getElementById('inst-month-list');const zeroNote=document.getElementById('inst-zero-note');const tenors=[3,6,12];const rates={BRI:{3:0.975,6:0.975,12:0.975},Mandiri:{3:0.984,6:0.9725,12:0.96},BCA:{3:0.97,6:0.94,12:0.91}};function formatRupiah(num){return 'Rp '+(Math.round(num)).toString().replace(/\B(?=(\d{3})+(?!\d))/g,'.')}function activeBank(){const tab=card.querySelector('.bank-tab.active');return tab?tab.dataset.bank:'BRI'}
13172
13173function _0x4o5p(bank){const a=getBasePrice();listEl.innerHTML='';const b=rates[bank]||{};let c=false;tenors.forEach(m=>{let d=b[m]??1;if(zeroEligible&&(bank==='BRI'||bank==='Mandiri')){d=1;c=true}const e=a/d;const f=m>0?Math.ceil((e/m)/100)*100:0;const g=document.createElement('li');g.className='inst-month-item';g.innerHTML=`<span class="tenor-badge">${m}x</span><span class="amount">${formatRupiah(f)} / bln</span>${zeroEligible&&(bank==='BRI'||bank==='Mandiri')?'<span class="tag tag-zero">0%</span>':''}`;listEl.appendChild(g)});zeroNote.style.display=c?'block':'none'}
13174
13175card.addEventListener('click',function(ev){const a=ev.target.closest('.bank-tab');if(!a)return;card.querySelectorAll('.bank-tab').forEach(b=>{b.classList.remove('active');b.setAttribute('aria-selected','false')});a.classList.add('active');a.setAttribute('aria-selected','true');_0x4o5p(a.dataset.bank)});
13176
13177window.instSetBasePrice=function(newPrice){setBasePrice(newPrice);_0x4o5p(activeBank())};_0x4o5p(activeBank());
13178})();
13179
13180/* ====== Fokus gambar varian ====== */
13181function _0x6q7r(s){return String(s||'').toLowerCase().replace(/[^a-z0-9]+/g,'')}function _0x8s9t(u){try{return new URL(u,window.location.origin).pathname.split('/').pop().toLowerCase()}catch(e){try{return(u||'').split('/').pop().toLowerCase()}catch(_){return ''}}}
13182function _0xau1v(url,altOpt){const a=document.getElementById('pd-main-img'),b=document.getElementById('pd-thumb-gallery'),c=document.getElementById('pd-mobile-slider');if(a&&url){a.src=url;if(altOpt)a.alt=altOpt}if(b){const d=_0x8s9t(url);let e=null;b.querySelectorAll('.pd-thumb-btn').forEach(btn=>{const f=btn.getAttribute('data-full')||'',g=_0x8s9t(f)===d;if(g)e=btn;btn.classList.toggle('active',g)});if(e){const h=e.getAttribute('data-alt')||'',i=e.getAttribute('data-slug')||'';if(h&&a)a.alt=h;if(i&&a)a.dataset.slug=i}}
13183  if (c){
13184    const targetName = _0x8s9t(url);
13185    const slideImg = Array.from(c.querySelectorAll('img')).find(im => _0x8s9t(im.src) === targetName);
13186    if (slideImg){
13187      
13188      try {
13189        const container = c;
13190        const imgRect = slideImg.getBoundingClientRect();
13191        const contRect = container.getBoundingClientRect();
13192        const offsetLeft = slideImg.offsetLeft - container.offsetLeft;
13193        const center = offsetLeft - (container.clientWidth - slideImg.clientWidth) / 2;
13194        container.scrollTo({ left: Math.max(0, center), behavior: 'smooth' });
13195      } catch(_){
13196        
13197        slideImg.scrollIntoView({ behavior:'smooth', inline:'center', block:'nearest' });
13198      }
13199    }
13200  }
13201}
13202function findImageForVariant(variant){
13203  if (!variant || !variant.attrs) return null;
13204  const tokens = [];
13205  try{
13206    Object.values(variant.attrs).forEach(v=>{
13207      if (!v) return;
13208      const raw = String(v);
13209      tokens.push(raw);
13210      tokens.push(normalize(raw));
13211    });
13212  }catch(_){}
13213  if (variant.sku){
13214    tokens.push(String(variant.sku));
13215    tokens.push(normalize(String(variant.sku)));
13216  }
13217  if (variant.image_url) {
13218      tokens.push(String(variant.image_url));
13219  }
13220  let best = null;
13221  let bestScore = -1;
13222
13223  for (const g of (galleryData||[])){
13224    const slugN = normalize(g.slug||'');
13225    const altN  = normalize(g.alt||'');
13226    const file  = _0x8s9t(g.url||'');
13227    const fileN = normalize(file);
13228    const imgU  = g.url;
13229
13230    for (const t of tokens){
13231      const tN = normalize(t);
13232      if (!tN) continue;
13233
13234      let score = -1;
13235      if (imgU === tN) score = 300;
13236      if (slugN && slugN === tN) score = 300;
13237      else if (fileN && fileN.includes(tN)) score = 200;
13238      else if (altN && altN.includes(tN)) score = 120;
13239
13240      if (score > bestScore){
13241        bestScore = score;
13242        best = g;
13243      }
13244      if (bestScore >= 300) break;
13245    }
13246    if (bestScore >= 300) break;
13247  }
13248  return bestScore >= 120 ? best : null;
13249}
13250function _0xey3z(variant){const a=_0xcw2x(variant);if(a&&a.url){_0xau1v(a.url,a.alt||'')}}
13251
13252/* ====== Layout mobile ====== */
13253(function _0xg04h(){const a=document.querySelector('.pd-media-card'),b=document.getElementById('pd-price-block'),c=document.getElementById('pd-price-anchor'),d=document.querySelector('.pd-info-card'),e=document.getElementById('pd-title'),f=document.getElementById('pd-variant-combined'),g=document.getElementById('pd-variants'),h=document.getElementById('pd-variants-anchor'),i=document.getElementById('pd-variant-summary'),j=document.getElementById('pd-summary-anchor'),titleRow=document.querySelector('.pd-title-row'),promoBanner=document.querySelector('.promo-endyear-mini-banner'),compareSection=document.querySelector('.pd-compare-section');if(!a||!b||!c||!d||!e)return;
13254
13255    let pricePlaced = false;
13256    let varPlaced = false;
13257    let titlePlaced = false;
13258    let promoPlaced = false;
13259    const mq = window.matchMedia('(max-width: 640px)');
13260
13261    function apply(){
13262        if (mq.matches) {
13263            if (!titlePlaced && a && titleRow && d.contains(titleRow)) { try{a.appendChild(titleRow);titlePlaced=true;}catch(_){} }
13264            if (!pricePlaced && a && b) { try{a.appendChild(b);pricePlaced=true;}catch(_){} }
13265            if (!varPlaced && f && d && e) {
13266                try{
13267                    f.hidden = false;
13268                    if (i && !f.contains(i)) f.appendChild(i);
13269                    if (g && !f.contains(g)) f.appendChild(g);
13270                    if(d.contains(e))d.insertBefore(f, e);
13271                    varPlaced = true;
13272                }catch(_){}
13273            }
13274            if (!promoPlaced && promoBanner && compareSection && compareSection.parentNode) {
13275                try{
13276                    compareSection.parentNode.insertBefore(promoBanner, compareSection);
13277                    promoPlaced = true;
13278                }catch(_){}
13279            }
13280        } else {
13281            if (titlePlaced && titleRow && d) {
13282                try{
13283                    const vcombined = document.getElementById('pd-variant-combined');
13284                    if (vcombined && d.contains(vcombined)) {
13285                        d.insertBefore(titleRow, vcombined);
13286                    } else {
13287                        d.insertBefore(titleRow, d.firstChild);
13288                    }
13289                    titlePlaced=false;
13290                }catch(_){}
13291            }
13292            if (pricePlaced && b && c && c.parentNode) {
13293                try{c.parentNode.insertBefore(b, c.nextSibling);pricePlaced=false;}catch(_){}
13294            }
13295            if (varPlaced) {
13296                try{
13297                    if (i && j && j.parentNode && !j.parentNode.contains(i)) j.parentNode.insertBefore(i, j.nextSibling);
13298                    if (g && h && h.parentNode && !h.parentNode.contains(g)) h.parentNode.insertBefore(g, h.nextSibling);
13299                    if (f) { f.hidden = true; f.innerHTML = ''; }
13300                    varPlaced = false;
13301                }catch(_){}
13302            }
13303            if (promoPlaced && promoBanner && b) {
13304                try{
13305                    b.appendChild(promoBanner);
13306                    promoPlaced = false;
13307                }catch(_){}
13308            }
13309        }
13310    }
13311
13312    mq.addEventListener ? mq.addEventListener('change', apply) : mq.addListener(apply);
13313    apply();
13314
13315    const fab = document.getElementById('pd-mobile-fab');
13316    if (fab) {
13317        const pid = parseInt(fab.getAttribute('data-pid')||'0',10) || 325;
13318        const btnCart = document.getElementById('mf-cart');
13319
13320        if (btnCart) btnCart.addEventListener('click', function(){
13321            const varInput = document.getElementById('pd-variant-id');
13322            const variantId = varInput ? parseInt(varInput.value||'0',10) : 0;
13323            try { if (window.addToCart) addToCart(pid, 1, variantId); } catch(_){}
13324            const variantText = getVariantSummaryText();
13325            const skuText = (function(){ const el=document.getElementById('pd-sku'); return el?el.textContent.trim():PD_SKU; })();
13326            showToast({
13327                type: 'success',
13328                title: 'Berhasil ditambahkan ke Keranjang',
13329                message: `<strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}<br>SKU: ${escapeHtml(skuText)} · Qty: 1`,
13330                duration: 5000,
13331                actions: [
13332                    { label: 'Lihat Keranjang', onClick: ()=> { window.location.href = '/keranjang'; } },
13333                    { label: 'Lanjut Belanja', variant: 'alt', onClick: ()=>{} }
13334                ]
13335            });
13336        });
13337    }
13338})();
13339
13340/* ====== Auto-hide installment banner if sidebar too tall (Desktop only) ====== */
13341(function(){
13342    function adjustSidebarBanner() {
13343        if (window.innerWidth <= 768) return;
13344        const sidebar = document.querySelector('.pd-sidebar');
13345        const infoCard = document.querySelector('.pd-info-card');
13346        const banner = document.querySelector('.pd-side-card.inst-alt-banner.inst-alt-banner--compact');
13347        if (!sidebar || !infoCard || !banner) return;
13348        const sidebarHeight = sidebar.offsetHeight;
13349        const infoCardHeight = infoCard.offsetHeight;
13350        if (sidebarHeight > infoCardHeight) {
13351            banner.style.display = 'none';
13352        } else {
13353            banner.style.display = '';
13354        }
13355    }
13356    if (document.readyState === 'loading') {
13357        document.addEventListener('DOMContentLoaded', adjustSidebarBanner);
13358    } else {
13359        adjustSidebarBanner();
13360    }
13361    let resizeTimer;
13362    window.addEventListener('resize', function() {
13363        clearTimeout(resizeTimer);
13364        resizeTimer = setTimeout(adjustSidebarBanner, 250);
13365    });
13366    const pwpSection = document.getElementById('pwp-deals-section');
13367    if (pwpSection) {
13368        const observer = new MutationObserver(function(mutations) {
13369            let shouldRecheck = false;
13370            mutations.forEach(function(mutation) {
13371                if (mutation.type === 'attributes' && mutation.attributeName === 'hidden') {
13372                    shouldRecheck = true;
13373                } else if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
13374                    shouldRecheck = true;
13375                }
13376            });
13377            if (shouldRecheck) {
13378                setTimeout(adjustSidebarBanner, 100);
13379            }
13380        });
13381        observer.observe(pwpSection, {
13382            attributes: true,
13383            childList: true,
13384            subtree: true
13385        });
13386    }
13387    window.adjustSidebarBanner = adjustSidebarBanner;
13388})();
13389
13390/* ====== Global constants ====== */
13391const IS_DIGITAL=false;
13392const IS_PREORDER=false;
13393
13394/* ====== Variants state (REVISED) ====== */
13395(function initVariants(){
13396const _0x7g8h=[];window.variants=_0x7g8h;
13397window.isPreorderProduct=false;
13398    const attrOrder = [];
13399
13400    if (!variants.length || !attrOrder.length) return;
13401
13402    function comboKey(sel){
13403        return attrOrder.map(a => a + '=' + (sel[a] ?? '')).join('|');
13404    }
13405
13406    const variantMap = {};
13407    variants.forEach(v=>{
13408        const key = comboKey(v.attrs);
13409        variantMap[key] = v;
13410    });
13411
13412    function readSelection(){
13413        const sel = {};
13414        attrOrder.forEach(a=>{
13415            const r = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"] input[type=radio]:checked`);
13416            sel[a] = r ? r.value : '';
13417        });
13418        return sel;
13419    }
13420
13421    function findVariant(sel){
13422        return variantMap[comboKey(sel)] || null;
13423    }
13424
13425    function autoFillMissing(sel){
13426        let changed = false;
13427        for (const a of attrOrder) {
13428            if (sel[a]) continue;
13429            const possible = [];
13430            const seen = new Set();
13431            variants.forEach(v=>{
13432                let ok = true;
13433                for (const a2 of attrOrder) {
13434                    if (a2 === a) continue;
13435                    if (sel[a2] && v.attrs[a2] !== sel[a2]) { ok = false; break; }
13436                }
13437                if (ok) {
13438                    const val = v.attrs[a];
13439                    if (!seen.has(val)) { seen.add(val); possible.push(val); }
13440                }
13441            });
13442            if (possible.length === 1) {
13443                const val = possible[0];
13444                const input = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"] input[type=radio][value="${CSS.escape(val)}"]`);
13445                if (input && !input.checked) { input.checked = true; sel[a] = val; changed = true; }
13446            }
13447        }
13448        return changed;
13449    }
13450
13451    function rupiah(n){
13452        n = parseInt(n||0,10)||0;
13453        return 'Rp ' + n.toString().replace(/\B(?=(\d{3})+(?!\d))/g,'.');
13454    }
13455    
13456    function focusGalleryByUrl(imageUrl) {
13457        const isMobile = window.matchMedia && window.matchMedia('(max-width: 640px)').matches;
13458          if (isMobile) {
13459            _0xau1v(imageUrl);
13460            return;
13461          }
13462        
13463          const gallery = document.getElementById('pd-thumb-gallery');
13464          const targetName = _0x8s9t(imageUrl);
13465        
13466          if (gallery) {
13467            const btns = Array.from(gallery.querySelectorAll('.pd-thumb-btn'));
13468            const btn = btns.find(b => _0x8s9t(b.getAttribute('data-full') || '') === targetName);
13469            if (btn) { btn.click(); return; }
13470          }
13471          
13472          _0xau1v(imageUrl);
13473    }
13474    
13475
13476function formatStockLabel(n){n=parseInt(n||0,10)||0;return(n>10)?'10+':String(n)}
13477function updateBranchPopover(v){try{if(!v)return;const a=document.getElementById('sp-qty-meg'),b=document.getElementById('sp-qty-mdf'),c=document.getElementById('sp-qty-bda');if(a)a.textContent=formatStockLabel(Math.max(0,v.smm|0));if(b)b.textContent=formatStockLabel(Math.max(0,v.smf|0));if(c)c.textContent=formatStockLabel(Math.max(0,v.sba|0))}catch(_){}}
13478
13479    function calcVoucherDisc(type,value,price,maxDisc){if(type==='PERCENTAGE'){let d=(price*value)/100;if(maxDisc>0&&d>maxDisc)d=maxDisc;return Math.round(d)}return Math.round(value)}
13480
13481    function applyVariant(v, focusImage = true){
13482        const elCur  = document.getElementById('pd-price-current');
13483        const elOri  = document.getElementById('pd-price-original');
13484        const elDisc = document.getElementById('pd-discount-badge');
13485        const elSku  = document.getElementById('pd-sku');
13486        const elStock= document.getElementById('pd-stock-badge');
13487        const elQty  = document.getElementById('qty');
13488        const btnAdd = document.getElementById('btn-add-cart');
13489        const btnBuy = document.getElementById('btn-buy');
13490        const hidVar = document.getElementById('pd-variant-id');
13491
13492        const mfCart= document.getElementById('mf-cart');
13493        const mfBuy = document.getElementById('mf-buy');
13494        const fab = document.getElementById('pd-mobile-fab');
13495
13496        if (hidVar) hidVar.value = v ? v.id : 0;
13497        if (elCur) elCur.textContent = rupiah(v ? v.final : 0);
13498        if (elSku && v && v.sku) elSku.textContent = v.sku;
13499
13500        if (v && v.ori > v.final) {
13501            if (elOri) { elOri.style.display='inline'; elOri.textContent = rupiah(v.ori); }
13502            if (elDisc) { elDisc.style.display='inline'; elDisc.textContent = '-' + (v.disc||0) + '%'; }
13503        } else {
13504            if (elOri) elOri.style.display='none';
13505            if (elDisc) elDisc.style.display='none';
13506        }
13507
13508        const stock = v ? (parseInt(v.stock||0,10)||0) : 0;
13509        let effectiveStock = stock;
13510        let isActivePreorder = false;
13511        if (window.isPreorderProduct && v && 'preorder_remaining' in v && v.preorder_remaining !== null) {
13512            effectiveStock = parseInt(v.preorder_remaining, 10);
13513            if (isNaN(effectiveStock)) effectiveStock = 0;
13514            isActivePreorder = true;
13515        }
13516        if (elStock){
13517            elStock.classList.remove('in','out');
13518            if (effectiveStock>0){ 
13519                elStock.classList.add('in'); 
13520                elStock.textContent = IS_DIGITAL ? 'Tersedia' : (isActivePreorder ? 'Sisa Slot: ' + effectiveStock : 'Stok: ' + formatStockLabel(effectiveStock)); 
13521            } else { 
13522                elStock.classList.add('out'); 
13523                elStock.textContent = 'Habis'; 
13524            }
13525        }
13526        if (elQty){
13527            elQty.max = String(effectiveStock>0?effectiveStock:0);
13528            if (effectiveStock<=0) { elQty.value = '0'; elQty.disabled = true; }
13529            else { if (parseInt(elQty.value||'1',10) < 1) elQty.value='1'; elQty.disabled=false; }
13530        }
13531        
13532        const shouldDisable = !(v && effectiveStock>0);
13533        const updateButtons = () => {
13534            const btnAddCart = document.getElementById('btn-add-cart');
13535            const btnBuyNow = document.getElementById('btn-buy');
13536            const mobileCart = document.getElementById('mf-cart');
13537            const mobileBuy = document.getElementById('mf-buy');
13538            [btnAddCart, btnBuyNow, mobileCart, mobileBuy].forEach(btn => {
13539                if (!btn) return;
13540                if (shouldDisable) {
13541                    btn.disabled = true;
13542                    btn.setAttribute('disabled', 'disabled');
13543                    btn.classList.add('stock-disabled');
13544                    btn.setAttribute('data-stock-blocked', 'true');
13545                    btn.setAttribute('data-stock-reason', window.isPreorderProduct ? 'Pre-order habis' : 'Stok habis');
13546                } else {
13547                    btn.disabled = false;
13548                    btn.removeAttribute('disabled');
13549                    btn.classList.remove('stock-disabled');
13550                    btn.removeAttribute('data-stock-blocked');
13551                    btn.removeAttribute('data-stock-reason');
13552                }
13553            });
13554        };
13555        updateButtons();
13556        setTimeout(updateButtons, 10);
13557        setTimeout(updateButtons, 50);
13558        setTimeout(updateButtons, 100);
13559        if (mfBuy && v) {
13560            const voucherBtn = document.getElementById('btn-buy-voucher');
13561            const voucherCode = fab ? fab.dataset.voucherCode : '';
13562            let voucherDiscount = 0;
13563            
13564            if (voucherBtn && voucherCode) {
13565                const vType = voucherBtn.dataset.voucherType;
13566                const vValue = parseFloat(voucherBtn.dataset.voucherValue) || 0;
13567                const vMax = parseFloat(voucherBtn.dataset.voucherMax) || 0;
13568                const finalPrice = v.final || 0;
13569                if (vType && vValue > 0) {
13570                    voucherDiscount = calcVoucherDisc(vType, vValue, finalPrice, vMax);
13571                    if (fab) fab.dataset.voucherValue = voucherDiscount.toString();
13572                }
13573            }
13574            
13575            const finalPrice = v.final || 0;
13576            const textEl = mfBuy.querySelector('.mf-buy-text');
13577            const priceEl = mfBuy.querySelector('.mf-buy-price');
13578            
13579            if (effectiveStock > 0) {
13580                    if (IS_PREORDER) {
13581                        if (textEl) textEl.textContent = 'Pre Order Sekarang';
13582                        if (priceEl) priceEl.textContent = rupiah(finalPrice);
13583                    } else if (voucherCode && voucherDiscount > 0) {
13584                        if (textEl) textEl.textContent = 'Beli dengan Voucher';
13585                        if (priceEl) priceEl.textContent = rupiah(Math.max(0, finalPrice - voucherDiscount));
13586                    } else {
13587                        if (textEl) textEl.textContent = 'Beli Sekarang';
13588                        if (priceEl) priceEl.textContent = rupiah(finalPrice);
13589                    }
13590                } else {
13591                    if (textEl) textEl.textContent = 'Stok Habis';
13592                    if (priceEl) priceEl.textContent = rupiah(finalPrice);
13593                }
13594            }
13595        
13596        const isMobile = window.initMobile ?? 0;
13597        if (window.instSetBasePrice) window.instSetBasePrice(v ? (isMobile == '1' ? v.ori : v.final) : 0);
13598        if (window.updateSoldBySku) window.updateSoldBySku(v.sku);
13599        updateBranchPopover(v);
13600        if (v && v.image_url && focusImage) focusGalleryByUrl(v.image_url);
13601        if (v) {
13602            window.dispatchEvent(new CustomEvent('pd-variant-changed', {
13603                detail: {
13604                    price: v.final,
13605                    originalPrice: v.ori,
13606                    discount: v.disc,
13607                    stock: effectiveStock,
13608                    sku: v.sku,
13609                    variant_id: v.id
13610                }
13611            }));
13612            
13613            const encodedHash = encodeVariantHash(v.id);
13614            if (encodedHash && window.history && window.history.replaceState) {
13615                const newHash = '#v=' + encodedHash;
13616                const newUrl = window.location.pathname + window.location.search + newHash;
13617                window.history.replaceState(null, '', newUrl);
13618            }
13619        }
13620    }
13621
13622    let sel = readSelection();
13623    if (autoFillMissing(sel)) sel = readSelection();
13624
13625    let v = findVariant(sel);
13626    if (!v) {
13627        const candidates = variants.filter(variant=>{
13628            return attrOrder.every(a=>{
13629                if (!sel[a]) return true;
13630                return variant.attrs[a] === sel[a];
13631            });
13632        });
13633        if (candidates.length >= 1) {
13634            v = candidates[0];
13635            attrOrder.forEach(a=> sel[a] = v.attrs[a]);
13636            attrOrder.forEach(a=>{
13637                const val = sel[a];
13638                const input = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"] input[type=radio][value="${CSS.escape(val)}"]`);
13639                if (input) input.checked = true;
13640            });
13641        }
13642    }
13643
13644    const initSel = readSelection();
13645    const initVar = findVariant(initSel) || variants[0] || null;
13646    if (document.readyState === 'loading') {
13647        document.addEventListener('DOMContentLoaded', () => applyVariant(initVar, false));
13648    } else {
13649        applyVariant(initVar, false);
13650    }
13651    
13652function updateSummary(sel){Object.entries(sel).forEach(([a,val])=>{const el=document.getElementById('vs-'+a);if(el)el.textContent=val||'-'})}
13653    function refreshOptions(sel){
13654        attrOrder.forEach(a=>{
13655            const group = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"]`);
13656            if (!group) return;
13657            const chips = group.querySelectorAll('.v-chip');
13658            chips.forEach(ch=>{
13659                const val = ch.getAttribute('data-value');
13660                const possible = variants.some(v=>{
13661                    if (v.attrs[a] !== val) return false;
13662                    for (const a2 of attrOrder) {
13663                        if (a2 === a) continue;
13664                        if (sel[a2] && v.attrs[a2] !== sel[a2]) return false;
13665                    }
13666                    return true;
13667                });
13668                const input = ch.querySelector('input[type=radio]');
13669                const disabled = !possible;
13670                if (input) input.disabled = disabled;
13671                ch.classList.toggle('disabled', disabled);
13672
13673                let candidateVariant = null;
13674                if (possible) {
13675                    candidateVariant = variants.find(v=>{
13676                        if (v.attrs[a] !== val) return false;
13677                        for (const a2 of attrOrder) {
13678                            if (a2 === a) continue;
13679                            if (sel[a2] && v.attrs[a2] !== sel[a2]) return false;
13680                        }
13681                        return true;
13682                    });
13683                }
13684                const outOfStock = candidateVariant ? (window.isPreorderProduct && candidateVariant.preorder_remaining !== null ? candidateVariant.preorder_remaining <= 0 : candidateVariant.stock <= 0) : false;
13685                ch.classList.toggle('out', !disabled && outOfStock);
13686                const badge = ch.querySelector('.chip-badge');
13687                if (badge) badge.hidden = !(candidateVariant && outOfStock);
13688            });
13689            group.querySelectorAll('.v-chip').forEach(ch=>{
13690                const input = ch.querySelector('input[type=radio]');
13691                ch.classList.toggle('selected', input && input.checked);
13692            });
13693        });
13694    }
13695    updateSummary(sel);
13696    refreshOptions(sel);
13697    document.querySelectorAll('.v-chip').forEach(chip=>{const input=chip.querySelector('input[type=radio]');if(input&&input.checked){chip.classList.add('selected');}});
13698
13699    const container = document.getElementById('pd-variants');
13700    container && container.addEventListener('change', function(ev){
13701        const radio = ev.target.closest('input[type=radio]');
13702        if (!radio) return;
13703        sel = readSelection();
13704        if (autoFillMissing(sel)) sel = readSelection();
13705        const chosen = findVariant(sel);
13706        applyVariant(chosen || null, true);
13707        
13708        updateSummary(sel);
13709        refreshOptions(sel);
13710    });
13711    
13712window.applyVariant=applyVariant;
13713})();
13714
13715(function cleanBrokenDescImages(){
13716    const containers = [
13717        document.querySelector('.pd-full-desc-body'),
13718        document.querySelector('.pd-short-desc')
13719    ].filter(Boolean);
13720
13721    containers.forEach(root => {
13722        const imgs = root.querySelectorAll('img');
13723        imgs.forEach(img => {
13724            if (img.complete && img.naturalWidth === 0) {
13725                img.remove();
13726                return;
13727            }
13728            img.addEventListener('error', () => {
13729                img.remove();
13730            }, { once:true });
13731        });
13732    });
13733})();
13734
13735/* ====== Scroll To Top ====== */
13736(function _0xi25j(){const a=document.getElementById('pd-scroll-top');if(!a)return;function b(){if(window.matchMedia('(max-width:640px)').matches){const d=document.querySelector('.bottom-nav'),e=document.querySelector('.pd-mobile-fab');if(!d&&!e){setTimeout(b,100);return}const f=d&&window.getComputedStyle(d).display!=='none'&&window.getComputedStyle(d).visibility!=='hidden',g=e&&window.getComputedStyle(e).display!=='none'&&window.getComputedStyle(e).visibility!=='hidden';let h;if(f&&g){h='calc(68px + 54px + env(safe-area-inset-bottom))'}else if(g){h='calc(68px + 8px + env(safe-area-inset-bottom))'}else if(f){h='calc(60px + env(safe-area-inset-bottom))'}else{h='calc(20px + env(safe-area-inset-bottom))'}a.style.setProperty('--scroll-top-bottom',h)}else{a.style.removeProperty('--scroll-top-bottom')}}function c(){const d=window.pageYOffset||document.documentElement.scrollTop||0,e=d>300;a.classList.toggle('visible',e);a.setAttribute('aria-hidden',e?'false':'true');b()}window.addEventListener('scroll',c,{passive:!0});window.addEventListener('resize',b,{passive:!0});setTimeout(b,100);c();a.addEventListener('click',function(){window.scrollTo({top:0,behavior:'smooth'})})})();
13737
13738/* Format stok 10+ */
13739function _0xk67l(n){n=parseInt(n||0,10)||0;return n>10?'10+':String(n)}
13740
13741/* Toggle popover stok per cabang */
13742(function _0xm89n(){const a=document.getElementById('pd-stock-info-btn'),b=document.getElementById('pd-stock-popover'),c=document.getElementById('pd-stock-popover-close');if(!a||!b)return;function d(){if(window.matchMedia('(min-width: 641px)').matches){const e=document.getElementById('pd-stock-badge');if(!e)return;const f=e.getBoundingClientRect(),g=window.pageXOffset||document.documentElement.scrollLeft||0,h=window.pageYOffset||document.documentElement.scrollTop||0;b.style.left=(f.left+g-10)+'px';b.style.top=(f.bottom+h+10)+'px'}else{b.style.left='';b.style.top=''}}
13743function e(){d();b.classList.add('show');a.setAttribute('aria-expanded','true');b.setAttribute('aria-hidden','false');document.addEventListener('click',g,{capture:true});window.addEventListener('resize',d,{passive:true});window.addEventListener('scroll',d,{passive:true})}function f(){b.classList.remove('show');a.setAttribute('aria-expanded','false');b.setAttribute('aria-hidden','true');document.removeEventListener('click',g,{capture:true});window.removeEventListener('resize',d);window.removeEventListener('scroll',d)}function g(h){if(b.contains(h.target)||a.contains(h.target))return;f()}
13744a.addEventListener('click',function(h){h.preventDefault();b.classList.contains('show')?f():e()});if(c)c.addEventListener('click',f)})();
13745
13746/* ===== Share Product Modal ===== */
13747(function(){const a=document.getElementById('pd-share-btn'),z=document.getElementById('mf-share'),b=document.getElementById('pd-share-modal-overlay'),c=document.getElementById('pd-share-close'),d=document.getElementById('pd-share-copy-url-btn'),e=document.getElementById('pd-share-url-input');if((!a&&!z)||!b)return;const f="https://loklokpro.com/",g="Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Win 11 Home/Eclipse Black/3Y",h="Rp 20.789.000";const discountPercent=1;let i='';if(discountPercent>0){i=`🔥 Lagi Promo Nih! Diskon ${discountPercent}%\n\n✨ ${g}\n💰 Cuma ${h} aja!\n\n🛒 Buruan cek sebelum kehabisan!\n👉 `}else{i=`✨ Ada barang bagus nih!\n\n📦 ${g}\n💸 Harga: ${h}\n\n🛍️ Yuk, langsung cek!\n👉 `}function j(){b.classList.add('show');document.body.style.overflow='hidden'}function k(){b.classList.remove('show');document.body.style.overflow=''}if(a)a.addEventListener('click',j);if(z)z.addEventListener('click',j);if(c)c.addEventListener('click',k);b.addEventListener('click',function(l){if(l.target===b)k()});document.addEventListener('keydown',function(l){if(l.key==='Escape'&&b.classList.contains('show'))k()});b.addEventListener('click',function(l){const m=l.target.closest('[data-share]');if(!m)return;const n=m.getAttribute('data-share'),o=encodeURIComponent(f),p=e
13747ncodeURIComponent(i),q=encodeURIComponent(g);let r='';switch(n){case 'whatsapp':r=`https://wa.me/?text=${p}${o}`;break;case 'facebook':r=`https://www.facebook.com/sharer/sharer.php?u=${o}`;break;case 'twitter':r=`https://twitter.com/intent/tweet?text=${p}${o}`;break;case 'telegram':r=`https://t.me/share/url?url=${o}&text=${p}`;break;case 'email':r=`mailto:?subject=${encodeURIComponent('🔥 Cek Produk Keren Ini!')}&body=${p}${o}`;window.location.href=r;k();return;case 'copy':s();return;case 'pdf':const ps=document.querySelector('.product-single');if(ps){ps.setAttribute('data-print-date',new Date().toLocaleDateString('id-ID',{year:'numeric',month:'long',day:'numeric'}))}k();setTimeout(function(){window.print();try{if(window.ga4Ecomm&&window.ga4Ecomm.trackEvent)window.ga4Ecomm.trackEvent('share',{method:'pdf_export',content_type:'product',item_id:"SB0B3T"})}catch(_){}},300);return}if(r){window.open(r,'_blank','noopener,noreferrer');try{if(window.ga4Ecomm&&window.ga4Ecomm.trackEvent)window.ga4Ecomm.trackEvent('share',{method:n,content_type:'product',item_id:"SB0B3T"})}catch(_){}}});function s(){if(!e)return;e.select();e.setSelectionRange(0,99999);try{if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(f).then(function(){t()}).catch(function(){document.execCommand('copy');t()})}else{document.execCommand('copy');t()}}catch(u){console.error('Failed to copy:',u)}}function t(){if(!d)return;const v=d.innerHTML;d.classList.add('copied');d.innerHTML='<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg><span class="pd-share-copy-text">Tersalin!</span>';if(window.showToast)showToast({type:'success',title:'✨ Link Tersalin!',message:'Link produk berhasil disalin. Yuk bagikan ke teman!',duration:3000});setTimeout(function(){d.classList.remove('copied');d.innerHTML=v},2000)}if(d)d.addEventListener('click',s);if(navigator.share){const shareHandler=function(w){w.preventDefault();navigator.share({title:g,text:i+f,url:f}).catch(function(x){console.log('Native share cancelled or failed:',x)})};if(a)a.addEventListener('dblclick',shareHandler);if(z)z.addEventListener('dblclick',shareHandler)}})();
13748
13749/* ===== Embedding variants (server → client) ===== */
13750(function(){
13751  window.__PD_VARIANTS_WITH_BRANCH__ = [];
13752})();
13753
13754/* ===== Helper: update angka popover ===== */
13755function updateStockPopoverForVariant(v){
13756  try{
13757    const elMeg = document.getElementById('sp-qty-meg');
13758    const elMdf = document.getElementById('sp-qty-mdf');
13759    const elBda = document.getElementById('sp-qty-bda');
13760
13761    if (typeof v?.smm === 'number' && elMeg) elMeg.textContent = formatStockLabel(Math.max(0, v.smm|0));
13762    if (typeof v?.smf === 'number' && elMdf) elMdf.textContent = formatStockLabel(Math.max(0, v.smf|0));
13763    if (typeof v?.sba === 'number' && elBda) elBda.textContent = formatStockLabel(Math.max(0, v.sba|0));
13764  }catch(_){}
13765}
13766
13767/* ===== applyVariant: 10+ dan update popover ===== */
13768(function patchApplyVariant(){
13769  if (typeof applyVariant !== 'function') return;
13770  const original = applyVariant;
13771  window.applyVariant = function(v){
13772    original(v);
13773    
13774    try {
13775      const elStock = document.getElementById('pd-stock-badge');
13776      if (elStock){
13777        let txt = elStock.textContent || '';
13778        let m = txt.match(/\d+/);
13779        if (m) {
13780          const n = parseInt(m[0]||'0',10)||0;
13781          elStock.textContent = (n>0) ? ('Stok: ' + formatStockLabel(n)) : 'Habis';
13782        }
13783      }
13784    } catch(_){}
13785if(window.__PD_VARIANTS_WITH_BRANCH__&&v){const vv=window.__PD_VARIANTS_WITH_BRANCH__.find(x=>x.id===v.id)||v;updateStockPopoverForVariant(vv)}
13786  };
13787})();
13788
13789(function initCurrentVariantBranch(){
13790  const list = window.__PD_VARIANTS_WITH_BRANCH__ || [];
13791  if (!list.length) return;
13792  const hid = document.getElementById('pd-variant-id');
13793  const vid = hid ? parseInt(hid.value||'0',10) : 0;
13794  const current = list.find(x => x.id === vid) || list[0] || null;
13795  if (current) updateStockPopoverForVariant(current);
13796})();
13797
13798(function(){
13799    if (!window.ga4Ecomm) return;
13800    const catSegs = ["Komputer & Laptop","Laptop","Gaming"];
13801    window.ga4Ecomm.viewItem({
13802      currency: 'IDR',
13803      value: 20789000,
13804      item_id: "SB0B3T",
13805      item_name: "Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Win 11 Home/Eclipse Black/3Y",
13806      item_brand: "Lenovo ",
13807      item_category: catSegs && catSegs.length ? catSegs.join(' / ') : ''
13808    });
13809  })();
13810  
13811(function patchApplyVariantFocus(){
13812  if (typeof applyVariant !== 'function') return;
13813  const original = applyVariant;
13814  window.applyVariant = function(v, focusImage = true){
13815    
13816    original(v, focusImage);
13817    
13818    if (focusImage && v && v.image_url) {
13819      _0xau1v(v.image_url);
13820    }
13821  };
13822})();
13823
13824(function() {
13825  var soldMap = [];
13826  var soldTotalLabel = "Terjual 2";
13827
13828  function formatSoldLabel(qty) {
13829    if (qty >= 1000000) {
13830      var v = Math.floor((qty / 100000) ) / 10;
13831      return 'Terjual ' + (String(v).replace('.', ',')) + 'jt+';
13832    }
13833    if (qty >= 1000) {
13834      var v2 = Math.floor((qty / 100) ) / 10;
13835      return 'Terjual ' + (String(v2).replace('.', ',')) + 'rb+';
13836    }
13837    if (qty >= 100) {
13838      var v2 = Math.floor((qty / 100) ) * 100;
13839      return 'Terjual ' + (String(v2).replace('.', ',')) + '+';
13840    }
13841    if (qty >= 10) {
13842      var v2 = Math.floor((qty / 10) ) * 10;
13843      return 'Terjual ' + (String(v2).replace('.', ',')) + '+';
13844    }
13845    return 'Terjual ' + (qty.toLocaleString('id-ID'));
13846  }
13847
13848  function updateSoldLabel(labelText) {
13849    var el = document.querySelector('#pd-sold-inline, [data-role="sold-chip"]');
13850    if (el) el.textContent = labelText;
13851  }
13852
13853  window.updateSoldBySku = function(sku) {
13854    if (!sku || typeof sku !== 'string') {
13855      updateSoldLabel(soldTotalLabel);
13856      return;
13857    }
13858    var qty = soldMap[sku] || 0;
13859    updateSoldLabel(formatSoldLabel(qty));
13860  };
13861
13862    updateSoldLabel(soldTotalLabel);
13863  })();
13864
13865(function(){const h=document.getElementById('pd-sticky-header');if(!h)return;const p=document.getElementById('pd-price-anchor');const ac=document.getElementById('pd-sticky-add-cart');const by=document.getElementById('pd-sticky-buy');let lt=window.scrollY;let tk=false;function u(){if(!p)return;const r=p.getBoundingClientRect();const s=window.scrollY;if(r.top<0){h.classList.add('visible')}else{h.classList.remove('visible')}lt=s;tk=false}function rt(){if(!tk){window.requestAnimationFrame(u);tk=true}}window.addEventListener('scroll',rt,{passive:true});u();function sy(){const ma=document.getElementById('btn-add-cart');const mb=document.getElementById('btn-buy');if(ac&&ma){ac.disabled=ma.disabled}if(by&&mb){by.disabled=mb.disabled}}sy();const ob=new MutationObserver(sy);const ma=document.getElementById('btn-add-cart');const mb=document.getElementById('btn-buy');if(ma)ob.observe(ma,{attributes:true,attributeFilter:['disabled']});if(mb)ob.observe(mb,{attributes:true,attributeFilter:['disabled']});const ss=document.getElementById('pd-sticky-share-btn');const ms=document.getElementById('pd-share-btn');if(ss&&ms){ss.addEventListener('click',function(e){e.preventDefault();ms.click()})}if(ac){ac.addEventListener('click',function(e){e.preventDefault();if(ac.disabled)return;const m=document.getElementById('btn-add-cart');if(m&&!m.disabled){m.click()}})}if(by){by.addEventListener('click',function(e){e.preventDefault();if(by.disabled)return;const m=document.getElementById('btn-buy');if(m&&!m.disabled&&window.buyNow){window.buyNow(e)}})}
13866function getChatProductData(){const varInput=document.getElementById('pd-variant-id');const variantId=varInput?parseInt(varInput.value||'0',10):0;const variantSummary=getVariantSummaryText();const priceEl=document.getElementById('pd-price-current');const currentPrice=priceEl?priceEl.textContent.trim():'Rp 20.789.000';const pd={id:325,name:"Lenovo Legion Go 8ASP2 - 83N0000RID \/AMD Ryzen Z2 Extreme\/32GB\/1TB SSD\/AMD Radeon Graphics\/8.8\" WUXGA OLED Touch\/Win 11 Home\/Eclipse Black\/3Y",price:currentPrice,image:"\/uploads\/products\/lenovo-legion-go-8asp2-83n0000rid.webp",url:window.location.href};if(variantId>0){pd.variant_id=variantId}if(variantSummary){pd.variant_summary=variantSummary}return pd}
13867const ch=document.getElementById('pd-sticky-chat');if(ch){ch.addEventListener('click',function(e){e.preventDefault();if(!window.plazaChat){console.warn('Chat widget not loaded');return}window.plazaChat.attachProduct(getChatProductData());window.plazaChat.openWidget()})}const dch=document.getElementById('btn-chat-icon');if(dch){dch.addEventListener('click',function(e){e.preventDefault();if(!window.plazaChat){console.warn('Chat widget not loaded');return}window.plazaChat.attachProduct(getChatProductData());window.plazaChat.openWidget()})}const mch=document.getElementById('mf-chat');if(mch){mch.addEventListener('click',function(e){e.preventDefault();if(!window.plazaChat){console.warn('Chat widget not loaded');return}window.plazaChat.attachProduct(getChatProductData());window.plazaChat.openWidget()})}window.addEventListener('pd-variant-changed',function(e){const d=e.detail||{};if(d.price!==undefined){const sp=document.getElementById('pd-sticky-price');if(sp){sp.textContent=formatRupiah(d.price)}}if(d.originalPrice!==undefined&&d.originalPrice>d.price){const so=document.getElementById('pd-sticky-price-original');if(so){so.textContent=formatRupiah(d.originalPrice);so.style.display=''}}else{const so=document.getElementById('pd-sticky-price-original');if(so){so.style.display='none'}}if(d.discount!==undefined&&d.discount>0){const sd=document.getElementById('pd-sticky-discount');if(sd){sd.textContent='-'+d.discount+'%';sd.style.display=''}}else{const sd=document.getElementById('pd-sticky-discount');if(sd){sd.style.display='none'}}const hs=d.stock>0;if(ac){ac.disabled=!hs}if(by){by.disabled=!hs}});
13867function formatRupiah(n){return'Rp'+Math.floor(n).toLocaleString('id-ID')}})();
13868
13869(function(){const t=document.getElementById('pdv-toggle-btn');const v=document.getElementById('pd-voucher-section');if(t&&v){const h=v.querySelectorAll('.pdv-min-hidden');const n=h.length;t.addEventListener('click',function(){const e=t.classList.toggle('expanded');h.forEach(function(i){if(e){i.classList.remove('pdv-min-hidden')}else{i.classList.add('pdv-min-hidden')}});t.textContent=e?'Tutup':'+'+n})}const m=document.getElementById('voucher-detail-modal');const mc=document.getElementById('voucher-modal-close');const ma=document.getElementById('voucher-modal-cancel');const mcp=document.getElementById('voucher-modal-copy');if(m){const bb=document.querySelectorAll('.pdv-badge-btn');bb.forEach(function(btn){btn.addEventListener('click',function(){if(btn.disabled)return;const c=btn.getAttribute('data-voucher-code');const nm=btn.getAttribute('data-voucher-name');const dl=btn.getAttribute('data-discount-label');const bl=btn.getAttribute('data-badge-label');const mp=parseInt(btn.getAttribute('data-min-purchase'));const md=parseInt(btn.getAttribute('data-max-discount'));const vu=btn.getAttribute('data-valid-until');document.getElementById('voucher-modal-badge').innerHTML='<span class="vmb-value">'+dl+'</span><span class="vmb-label">'+bl+'</span>';document.getElementById('voucher-modal-code').textContent=c;document.getElementById('voucher-modal-name').textContent=nm;const me=document.getElementById('voucher-modal-min');if(mp>0){me.style.display='';document.getElementById('voucher-modal-min-val').textContent=fr(mp)}else{me.style.display='none'}const xe=document.getElementById('voucher-modal-max');if(md>0){xe.style.display='';document.getElementById('voucher-modal-max-val').textContent=fr(md)}else{xe.style.display='none'}if(vu){const d=new Date(vu);const ft=d.toLocaleDateString('id-ID',{day:'numeric',month:'long',year:'numeric',hour:'2-digit',minute:'2-digit'});document.getElementById('voucher-modal-valid-val').textContent=ft}mcp.setAttribute('data-code',c);m.classList.add('show');document.body.style.overflow='hidden'})});function cm(){m.classList.remove('show');document.body.style.overflow=''}if(mc)mc.addEventListener('click',cm);if(ma)ma.addEventListener('click',cm);m.addEventListener('click',function(e){if(e.target===m)cm()});if(mcp){mcp.addEventListener('click',function(){const c=mcp.getAttribute('data-code');if(!c)return;if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(c).then(function(){smc()}).catch(function(err){fc(c)})}else{fc(c)}})}}const b=document.querySelectorAll('.pdv-min-copy');b.forEach(function(btn){btn.addEventListener('click',function(){if(btn.disabled)return;const code=btn.getAttribute('data-code');if(!code)return;if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(code).then(function(){sc(btn)}).catch(function(err){fc(code,btn)})}else{fc(code,btn)}})});function fc(text,btn){const tx=document.createElement('textarea');tx.value=text;tx.style.position='fixed';tx.style.opacity='0';document.body.appendChild(tx);tx.select();try{document.execCommand('copy');if(btn)sc(btn);else smc()}catch(err){}document.body.removeChild(tx)}function sc(btn){const o=btn.textContent;btn.classList.add('copied');btn.textContent='✓ Tersalin';setTimeout(function(){btn.classList.remove('copied');btn.textContent=o},2000)}function smc(){const o=mcp.textContent;mcp.textContent='✓ Kode Tersalin';mcp.style.background='#22c55e';setTimeout(function(){mcp.textContent=o;mcp.style.background=''},2000)}function fr(a){return'Rp'+Math.floor(a).toLocaleString('id-ID')}})();
13870</script>
vendor: 1 bytes, line 13870
13870
13871<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
13872window.PD_ID=325;window.PD_NAME=_0x3c4d;window.PD_SKU=_0x5e6f;window.PD_CATEGORY_PATH="Komputer & Laptop/Laptop/Gaming";window.PD_BEST_VOUCHER=null;
13873window.IS_DIGITAL=IS_DIGITAL;
13874window.isPreorderProduct=false;
13875window.PD_PREORDER_DATA=null;
13876</script>
13876
13877
13878<script data-inlined="pd-lightbox.js">
13879// PlazaIT Product Lightbox (vanilla, no CDN) — v4 (swipe-down to close)
13880(function(){
13881  const state = {
13882    items: [],
13883    open: false,
13884    idx: 0,
13885    scale: 1,
13886    minScale: 1,
13887    maxScale: 4,
13888    tx: 0, ty: 0,
13889    isDragging: false,
13890    dragStartX: 0, dragStartY: 0,
13891    baseTx: 0, baseTy: 0,
13892    touchStartX: 0, touchStartY: 0,
13893    lastTap: 0,
13894    swipeMode: 'none', // 'none' | 'h' | 'v'
13895    totalDx: 0, totalDy: 0
13896  };
13897
13898  let root, imgEl, counterEl, btnPrev, btnNext, btnClose, canvas;
13899
13900  const ORIGIN = (location.origin || (location.protocol + '//' + location.host));
13901
13902  function absUrl(u){
13903    if (!u) return '';
13904    try {
13905      if (/^https?:\/\//i.test(u)) return u;
13906      return new URL(u, ORIGIN).href;
13907    } catch(_) { return u; }
13908  }
13909  function fileName(u){
13910    try { return new URL(u, ORIGIN).pathname.split('/').pop().toLowerCase(); }
13911    catch(_){ return String(u||'').split('/').pop().toLowerCase(); }
13912  }
13913
13914  function buildItems(){
13915    const gd = (window.galleryData || []).filter(Boolean);
13916    if (gd.length){
13917      state.items = gd.map(g => ({
13918        src: absUrl(g.url), alt: g.alt || '', slug: g.slug || '', w: g.w || 1200, h: g.h || 1200
13919      })).filter(it => !!it.src);
13920      return;
13921    }
13922    const thumbs = document.querySelectorAll('.pd-thumb-btn');
13923    state.items = Array.from(thumbs).map(btn => {
13924      const raw = btn.getAttribute('data-full') || '';
13925      return { src: absUrl(raw), alt: btn.getAttribute('data-alt') || '', slug: btn.getAttribute('data-slug') || '', w: 1200, h: 1200 };
13926    }).filter(it => !!it.src);
13927  }
13928
13929  function ensureUI(){
13930    if (root) return;
13931    root = document.createElement('div');
13932    root.className = 'pd-lb';
13933    root.id = 'pd-lightbox';
13934    root.setAttribute('role','dialog');
13935    root.setAttribute('aria-modal','true');
13936    root.setAttribute('aria-hidden','true');
13937    root.setAttribute('tabindex','-1');
13938
13939    // Canvas terlebih dulu (z-index di CSS membuat tombol di atas)
13940    root.innerHTML = `
13941      <div class="pd-lb-canvas"><img class="pd-lb-img" id="pd-lb-img" alt=""></div>
13942      <button class="pd-lb-close" aria-label="Tutup" title="Tutup">&times;</button>
13943      <button class="pd-lb-prev" aria-label="Sebelumnya" title="Sebelumnya">&#10094;</button>
13944      <button class="pd-lb-next" aria-label="Berikutnya" title="Berikutnya">&#10095;</button>
13945      <div class="pd-lb-counter" id="pd-lb-counter">1 / 1</div>
13946    `;
13947    document.body.appendChild(root);
13948
13949    imgEl     = root.querySelector('#pd-lb-img');
13950    counterEl = root.querySelector('#pd-lb-counter');
13951    btnPrev   = root.querySelector('.pd-lb-prev');
13952    btnNext   = root.querySelector('.pd-lb-next');
13953    btnClose  = root.querySelector('.pd-lb-close');
13954    canvas    = root.querySelector('.pd-lb-canvas');
13955
13956    // Button clicks
13957    btnPrev.addEventListener('click', (e)=>{ e.stopPropagation(); prev(); });
13958    btnNext.addEventListener('click', (e)=>{ e.stopPropagation(); next(); });
13959    btnClose.addEventListener('click', (e)=>{ e.stopPropagation(); close(); });
13960
13961    // Klik backdrop untuk tutup
13962    root.addEventListener('click', (e)=>{ if (e.target === root) close(); });
13963
13964    // Keyboard global
13965    document.addEventListener('keydown', (e)=>{
13966      if (!state.open) return;
13967      if (e.key === 'Escape') { e.preventDefault(); close(); }
13968      if (e.key === 'ArrowRight') { e.preventDefault(); next(); }
13969      if (e.key === 'ArrowLeft')  { e.preventDefault(); prev(); }
13970    });
13971
13972    // Wheel zoom (desktop)
13973    root.addEventListener('wheel', (e)=>{
13974      if (!state.open) return;
13975      e.preventDefault();
13976      const delta = -Math.sign(e.deltaY) * 0.2;
13977      zoomAt(delta, e.clientX, e.clientY);
13978    }, { passive: false });
13979
13980    // Drag pan (mouse)
13981    canvas.addEventListener('mousedown', (e)=>{
13982      if (state.scale <= 1) return;
13983      state.isDragging = true;
13984      state.dragStartX = e.clientX;
13985      state.dragStartY = e.clientY;
13986      state.baseTx = state.tx;
13987      state.baseTy = state.ty;
13988      e.preventDefault();
13989    });
13990    window.addEventListener('mousemove', (e)=>{
13991      if (!state.isDragging) return;
13992      const dx = e.clientX - state.dragStartX;
13993      const dy = e.clientY - state.dragStartY;
13994      setTranslate(state.baseTx + dx, state.baseTy + dy);
13995    });
13996    window.addEventListener('mouseup', ()=>{ state.isDragging = false; });
13997
13998    // Touch: swipe navigate (H), swipe-down close (V), double-tap zoom, drag pan
13999    canvas.addEventListener('touchstart', (e)=>{
14000      if (!state.open) return;
14001      if (e.touches.length === 1) {
14002        const t = e.touches[0];
14003        state.touchStartX = t.clientX;
14004        state.touchStartY = t.clientY;
14005        state.totalDx = 0;
14006        state.totalDy = 0;
14007        state.swipeMode = 'none';
14008
14009        state.isDragging = state.scale > 1;
14010        state.dragStartX = t.clientX;
14011        state.dragStartY = t.clientY;
14012        state.baseTx = state.tx;
14013        state.baseTy = state.ty;
14014
14015        // Double-tap toggle zoom
14016        const now = Date.now();
14017        if (now - state.lastTap < 300) {
14018          if (state.scale > 1) resetTransform(); else zoomAt(+1, t.clientX, t.clientY, 2);
14019          state.lastTap = 0;
14020          e.preventDefault();
14021          return;
14022        }
14023        state.lastTap = now;
14024      }
14025    }, { passive: true });
14026
14027    canvas.addEventListener('touchmove', (e)=>{
14028      if (!state.open) return;
14029
14030      // Drag pan saat zoom
14031      if (state.isDragging && e.touches.length === 1 && state.scale > 1) {
14032        e.preventDefault();
14033        const t = e.touches[0];
14034        const dx = t.clientX - state.dragStartX;
14035        const dy = t.clientY - state.dragStartY;
14036        setTranslate(state.baseTx + dx, state.baseTy + dy);
14037        return;
14038      }
14039
14040      // Gesture saat scale <= 1
14041      if (state.scale <= 1 && e.touches.length === 1) {
14042        const t = e.touches[0];
14043        const dx = t.clientX - state.touchStartX;
14044        const dy = t.clientY - state.touchStartY;
14045        state.totalDx = dx; state.totalDy = dy;
14046
14047        // Tentukan mode swipe di awal gerakan
14048        if (state.swipeMode === 'none') {
14049          const absX = Math.abs(dx), absY = Math.abs(dy);
14050          if (absY > 12 && absY > absX * 1.3) {
14051            state.swipeMode = 'v'; // swipe vertical (untuk close)
14052          } else if (absX > 12 && absX > absY * 1.3) {
14053            state.swipeMode = 'h'; // swipe horizontal (ganti slide)
14054          }
14055        }
14056
14057        if (state.swipeMode === 'v') {
14058          // Ikuti jari, kurangi opacity untuk feedback
14059          e.preventDefault();
14060          setRootDrag(dy);
14061        } else if (state.swipeMode === 'h') {
14062          // Biarkan sampai touchend untuk menentukan next/prev (tidak perlu preventDefault di sini)
14063        }
14064      }
14065    }, { passive: false });
14066
14067    canvas.addEventListener('touchend', (e)=>{
14068      if (!state.open) return;
14069
14070      if (state.scale <= 1) {
14071        const dx = state.totalDx;
14072        const dy = state.totalDy;
14073
14074        if (state.swipeMode === 'v') {
14075          const TH = 80; // threshold jarak piksel untuk menutup
14076          if (Math.abs(dy) > TH) {
14077            // Animasi keluar arah swipe
14078            animateClose(dy);
14079          } else {
14080            // Snap back
14081            resetRootDrag(true);
14082          }
14083        } else if (state.swipeMode === 'h') {
14084          if (Math.abs(dx) > 50) { dx < 0 ? next() : prev(); }
14085        }
14086      }
14087
14088      state.isDragging = false;
14089      state.swipeMode = 'none';
14090      state.totalDx = 0;
14091      state.totalDy = 0;
14092    });
14093  }
14094
14095  function setImage(idx){
14096    if (!state.items.length) return;
14097    state.idx = (idx + state.items.length) % state.items.length;
14098    const it = state.items[state.idx];
14099    imgEl.src = it.src;
14100    imgEl.alt = it.alt || '';
14101    counterEl.textContent = (state.idx + 1) + ' / ' + state.items.length;
14102    resetTransform();
14103    preloadNeighbors();
14104  }
14105
14106  function resetTransform(){
14107    state.scale = 1;
14108    state.tx = 0; state.ty = 0;
14109    applyTransform();
14110  }
14111
14112  function setTranslate(tx, ty){
14113    state.tx = tx; state.ty = ty;
14114    applyTransform();
14115  }
14116
14117  function applyTransform(){
14118    imgEl.style.transform = `translate3d(${state.tx}px, ${state.ty}px, 0) scale(${state.scale})`;
14119  }
14120
14121  function zoomAt(delta, clientX, clientY, snapTo){
14122    const old = state.scale;
14123    let next = (typeof snapTo === 'number') ? snapTo : (old + delta);
14124    next = Math.max(state.minScale, Math.min(state.maxScale, next));
14125
14126    if (next !== old) {
14127      const rect = imgEl.getBoundingClientRect();
14128      const cx = clientX - rect.left - rect.width/2;
14129      const cy = clientY - rect.top  - rect.height/2;
14130      state.tx -= cx * (next/old - 1);
14131      state.ty -= cy * (next/old - 1);
14132      state.scale = next;
14133      applyTransform();
14134    }
14135  }
14136
14137  function preloadNeighbors(){
14138    if (state.items.length < 2) return;
14139    const nextIdx = (state.idx + 1) % state.items.length;
14140    const prevIdx = (state.idx - 1 + state.items.length) % state.items.length;
14141    [nextIdx, prevIdx].forEach(i=>{
14142      const img = new Image(); img.src = state.items[i].src;
14143    });
14144  }
14145
14146  // ----- Swipe-down visual helpers -----
14147  function setRootDrag(dy){
14148    const damped = dy * 0.6; // redam gerakan agar lembut
14149    const abs = Math.abs(dy);
14150    const fade = Math.max(0, Math.min(1, 1 - abs / 300)); // transparansi sesuai jarak
14151    root.style.transform = `translate3d(0, ${damped}px, 0)`;
14152    root.style.opacity = String(0.95 * fade + 0.05); // jangan jadi 0 total sebelum close
14153  }
14154  function resetRootDrag(withAnim){
14155    if (withAnim) {
14156      root.style.transition = 'transform .18s ease, opacity .18s ease';
14157      root.style.transform = 'translate3d(0,0,0)';
14158      root.style.opacity = '1';
14159      setTimeout(()=>{ root.style.transition = ''; }, 190);
14160    } else {
14161      root.style.transition = '';
14162      root.style.transform = '';
14163      root.style.opacity = '';
14164    }
14165  }
14166  function animateClose(dy){
14167    root.style.transition = 'transform .18s ease, opacity .18s ease';
14168    const off = (dy > 0 ? window.innerHeight : -window.innerHeight);
14169    root.style.transform = `translate3d(0, ${off}px, 0)`;
14170    root.style.opacity = '0';
14171    setTimeout(()=>{ root.style.transition = ''; close(); }, 180);
14172  }
14173
14174  function setSiblingsInert(makeInert){
14175    // Inert semua saudara root (elemen lain di body) agar fokus tidak lari
14176    const nodes = Array.from(document.body.children).filter(el => el !== root);
14177    nodes.forEach(el=>{
14178      if (makeInert) {
14179        if (!el.hasAttribute('inert')) el.setAttribute('inert','');
14180      } else {
14181        if (el.hasAttribute('inert')) el.removeAttribute('inert');
14182      }
14183    });
14184  }
14185
14186  function open(idx){
14187    if (!state.items.length) buildItems();
14188    if (!state.items.length) return;
14189    ensureUI();
14190
14191    // Reset efek drag visual
14192    resetRootDrag(false);
14193
14194    document.body.style.overflow = 'hidden';
14195    root.classList.add('show');
14196    root.removeAttribute('aria-hidden'); // hindari aria-hidden saat fokus child
14197    setSiblingsInert(true);
14198
14199    state.open = true;
14200    setImage(typeof idx === 'number' ? idx : 0);
14201
14202    // Fokuskan dialog agar keyboard pasti tertangkap
14203    try { root.focus(); } catch(_){}
14204  }
14205
14206  function close(){
14207    state.open = false;
14208    root.classList.remove('show');
14209    root.setAttribute('aria-hidden','true');
14210    setSiblingsInert(false);
14211    document.body.style.overflow = '';
14212
14213    // Bersihkan transform/opacity
14214    resetRootDrag(false);
14215  }
14216
14217  function next(){ setImage(state.idx + 1); }
14218  function prev(){ setImage(state.idx - 1); }
14219
14220  function findIndexByUrl(url){
14221    const target = fileName(absUrl(url));
14222    const idx = state.items.findIndex(it => fileName(it.src) === target);
14223    return idx >= 0 ? idx : 0;
14224  }
14225
14226  function initPdLightbox(){
14227    buildItems();
14228    ensureUI();
14229
14230    // Klik gambar utama → buka LB di index yang sama
14231    const mainImg = document.getElementById('pd-main-img');
14232    if (mainImg) {
14233      mainImg.style.cursor = 'zoom-in';
14234      mainImg.addEventListener('click', ()=>{
14235        open(findIndexByUrl(mainImg.src));
14236      });
14237    }
14238
14239    // Klik gambar slider mobile → buka LB di gambar yang diketuk
14240    const slider = document.getElementById('pd-mobile-slider');
14241    if (slider) {
14242      slider.addEventListener('click', (e)=>{
14243        const im = e.target && e.target.tagName === 'IMG' ? e.target : e.target.closest('img');
14244        if (!im) return;
14245        open(findIndexByUrl(im.src));
14246      });
14247    }
14248
14249    // (Opsional) double-click pada thumbnail untuk buka langsung
14250    const thumbs = document.querySelectorAll('.pd-thumb-btn');
14251    thumbs.forEach((btn)=>{
14252      let last = 0;
14253      btn.addEventListener('click', ()=>{
14254        const now = Date.now();
14255        if (now - last < 300) {
14256          const src = btn.getAttribute('data-full') || '';
14257          open(findIndexByUrl(absUrl(src)));
14258          last = 0;
14259        } else {
14260          last = now;
14261        }
14262      });
14263    });
14264
14265    // Ekspos API
14266    window.PDLightbox = { open, close, next, prev, findIndexByUrl };
14267  }
14268
14269  if (document.readyState === 'loading') {
14270    document.addEventListener('DOMContentLoaded', initPdLightbox);
14271  } else {
14272    initPdLightbox();
14273  }
14274})();
14275</script>
vendor: 1 bytes, line 14275
14275
14276<script data-inlined="cart.js">
14277(function(){
14278  function $(s,ctx=document){ return ctx.querySelector(s); }
14279  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
14280  
14281  // CSRF token helper
14282  function getCsrfToken() {
14283    const metaTag = document.querySelector('meta[name="csrf-token"]');
14284    return metaTag ? metaTag.getAttribute('content') : '';
14285  }
14286  
14287  function guessPdMeta(){
14288    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : ($('#pd-title')?.textContent || '');
14289    const sku  = (function(){ const el=$('#pd-sku'); return el ? el.textContent.trim() : (window.PD_SKU||''); })();
14290    // SECURITY FIX: Remove price from client-side metadata (will be validated server-side)
14291    const imgEl = $('#pd-main-img');
14292    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
14293    return { name, sku, image_url }; // price removed for security
14294  }
14295
14296  async function addToCart(productId, qty=1, variantId=0, metaOpt){
14297    const meta = Object.assign(guessPdMeta(), metaOpt||{});
14298    const payload = {
14299      product_id: parseInt(productId||0,10) || 0,
14300      qty: parseInt(qty||1,10) || 1,
14301      variant_id: parseInt(variantId||0,10) || 0,
14302      meta
14303    };
14304    
14305    // Check if user is logged in
14306    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
14307                      document.body.classList.contains('logged-in') ||
14308                      window.userLoggedIn === true ||
14309                      document.body.dataset.userLoggedIn === 'true';
14310    
14311    // Skip localStorage untuk logged user - mereka pakai database
14312    if (!isLoggedIn && window.guestCart) {
14313      try {
14314        window.guestCart.add(payload.product_id, payload.qty, payload.variant_id, meta);
14315      } catch (e) {
14316        console.warn('Failed to add to localStorage cart:', e);
14317      }
14318    }
14319    
14320    const res = await fetch('/api/cart/add.php', {
14321      method: 'POST',
14322      headers: { 
14323        'Content-Type':'application/json',
14324        'X-CSRF-Token': getCsrfToken()
14325      },
14326      credentials: 'same-origin',
14327      body: JSON.stringify({
14328        ...payload,
14329        csrf_token: getCsrfToken()
14330      }),
14331    });
14332    const data = await res.json().catch(()=>({ ok:false, error:'Gagal menambahkan ke keranjang' }));
14333    if (!res.ok || !data.ok) {
14334      const msg = data && data.error ? data.error : 'Gagal menambahkan ke keranjang';
14335      throw new Error(msg);
14336    }
14337
14338    // Update badge di navbar dengan data dari response
14339    try {
14340      const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
14341      if (el && data.cart && typeof data.cart.items_count === 'number') {
14342        el.textContent = String(data.cart.items_count);
14343        el.classList.toggle('is-empty', data.cart.items_count <= 0);
14344      }
14345    } catch(_){}
14346
14347    // Siarkan event global (dipakai cart-badge.js dan halaman lain)
14348    try {
14349      document.dispatchEvent(new CustomEvent('cart:updated', { detail: data.cart || {} }));
14350    } catch(_){}
14351
14352    // Update CartBadge dengan data dari response (lebih reliable)
14353    try { 
14354      if (window.CartBadge && window.CartBadge.set) {
14355        window.CartBadge.set(data.cart.items_count||0);
14356      }
14357    } catch(_){}
14358
14359    // Tambahan: refresh cart counter setelah delay kecil untuk memastikan data tersinkronisasi
14360    setTimeout(() => {
14361      if (window.updateCartCounter) {
14362        window.updateCartCounter();
14363      }
14364    }, 100);
14365
14366    return data;
14367  }
14368
14369  // Function to update cart counter from server
14370  async function updateCartCounter(){
14371    try {
14372      const res = await fetch('/api/cart/get.php', { credentials:'same-origin' });
14373      const data = await res.json().catch(()=>({ok:false}));
14374      if (data.ok && typeof data.items_count === 'number') {
14375        const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
14376        if (el) {
14377          el.textContent = String(data.items_count);
14378          el.classList.toggle('is-empty', data.items_count <= 0);
14379        }
14380        // Dispatch event
14381        try {
14382          document.dispatchEvent(new CustomEvent('cart:updated', { detail: { items_count: data.items_count } }));
14383        } catch(_){}
14384      }
14385    } catch (err) {
14386      console.log('Could not update cart counter:', err);
14387    }
14388  }
14389
14390  // Function to manually merge guest cart (if needed)
14391  async function mergeGuestCart(){
14392    try {
14393      const res = await fetch('/api/cart/merge-guest.php', {
14394        method: 'POST',
14395        headers: { 
14396          'Content-Type': 'application/json',
14397          'X-CSRF-Token': getCsrfToken()
14398        },
14399        credentials: 'same-origin',
14400        body: JSON.stringify({ csrf_token: getCsrfToken() })
14401      });
14402      const data = await res.json().catch(()=>({ok:false}));
14403      
14404      if (data.ok && data.merged_items > 0) {
14405        console.log(`Guest cart merged: ${data.merged_items} items`);
14406        // Update cart counter after merge
14407        await updateCartCounter();
14408        return data;
14409      }
14410      return data;
14411    } catch (err) {
14412      console.log('Could not merge guest cart:', err);
14413      return { ok: false, error: err.message };
14414    }
14415  }
14416
14417  window.addToCart = addToCart;
14418  window.updateCartCounter = updateCartCounter;
14419  window.mergeGuestCart = mergeGuestCart;
14420
14421  // Optional: auto-bind untuk tombol apapun yang punya data-addtocart
14422  document.addEventListener('click', async (e)=>{
14423    const btn = e.target.closest('[data-addtocart]');
14424    if (!btn) return;
14425    e.preventDefault();
14426    if (btn.disabled) return;
14427
14428    const pid = parseInt(btn.getAttribute('data-pid')||'0',10);
14429    const vid = parseInt(btn.getAttribute('data-vid')||'0',10);
14430    const qty = parseInt(btn.getAttribute('data-qty')||'1',10) || 1;
14431
14432    try {
14433      const data = await addToCart(pid, qty, vid);
14434      if (window.showToast) {
14435        const meta = guessPdMeta();
14436        window.showToast({
14437          type:'success',
14438          title:'Berhasil ditambahkan',
14439          message:`<strong>${esc(meta.name)}</strong>${meta.sku?` · <em>SKU: ${esc(meta.sku)}</em>`:''}<br>Qty: ${qty}`,
14440          duration: 3800,
14441          actions: [
14442            { label:'Lihat Keranjang', onClick: ()=> { window.location.href='/keranjang'; } },
14443            { label:'Lanjut Belanja', variant:'alt', onClick: ()=>{} }
14444          ]
14445        });
14446      }
14447    } catch (err) {
14448      if (window.showToast) {
14449        window.showToast({ type:'warn', title:'Gagal', message: String(err?.message||'Gagal menambahkan'), duration: 4200 });
14450      } else {
14451        console.error(err);
14452      }
14453    }
14454  });
14455})();
14456</script>
vendor: 1 bytes, line 14456
14456
14457<script data-inlined="stock-validator-vanilla.js">
14458/**
14459 * Stock Validation - Vanilla JS Version
14460 * 
14461 * This script provides client-side stock validation without jQuery dependency
14462 * - Works with native JavaScript and fetch API
14463 * - Graceful fallback for older browsers
14464 * - Same functionality as jQuery version
14465 */
14466
14467window.StockValidator = (function() {
14468    'use strict';
14469
14470    const config = {
14471        apiEndpoint: '/api/stock/check.php',
14472        modalSelector: '#stock-modal-overlay',  // We create our own modal
14473        loadingClass: 'loading-stock',
14474        disabledClass: 'stock-disabled',
14475        debug: true  // Enable debug logging
14476    };
14477
14478    /**
14479     * Cross-browser event listener helper
14480     */
14481    function addEventListener(element, event, handler) {
14482        if (element.addEventListener) {
14483            element.addEventListener(event, handler);
14484        } else if (element.attachEvent) {
14485            element.attachEvent('on' + event, handler);
14486        }
14487    }
14488
14489    /**
14490     * Get stock information from embedded variant data (instant, no AJAX needed)
14491     */
14492    function getStockFromEmbeddedData(variantId) {
14493        // Check if embedded variant data exists
14494        if (!window.__PD_VARIANTS_WITH_BRANCH__ || !Array.isArray(window.__PD_VARIANTS_WITH_BRANCH__)) {
14495            return null;
14496        }
14497        
14498        const variants = window.__PD_VARIANTS_WITH_BRANCH__;
14499        
14500        // SPECIAL CASE: Simple product (no variants) - empty array means simple product
14501        if (variants.length === 0) {
14502            // For simple products, we cannot determine stock from embedded data
14503            // Return null to force API validation
14504            return null;
14505        }
14506        
14507        // If no variant specified, check base product stock (all variants)
14508        if (!variantId || variantId <= 0) {
14509            // For base product, check if ANY variant has stock
14510            const isPreorder = window.isPreorderProduct || false;
14511            let hasAnyStock = false;
14512            let totalStock = 0;
14513            
14514            if (isPreorder) {
14515                // For pre-order, check preorder_remaining
14516                hasAnyStock = variants.some(v => {
14517                    if ('preorder_remaining' in v && v.preorder_remaining !== null) {
14518                        return v.preorder_remaining > 0;
14519                    }
14520                    return v.stock > 0;
14521                });
14522                totalStock = variants.reduce((sum, v) => {
14523                    if ('preorder_remaining' in v && v.preorder_remaining !== null) {
14524                        return sum + (v.preorder_remaining || 0);
14525                    }
14526                    return sum + (v.stock || 0);
14527                }, 0);
14528            } else {
14529                hasAnyStock = variants.some(v => v.stock > 0);
14530                totalStock = variants.reduce((sum, v) => sum + (v.stock || 0), 0);
14531            }
14532            
14533            return {
14534                hasStock: hasAnyStock,
14535                stock: totalStock,
14536                sku: 'Base Product',
14537                variant: null
14538            };
14539        }
14540        
14541        // Find specific variant
14542        const variant = variants.find(v => v.id === variantId);
14543        
14544        if (!variant) {
14545            return {
14546                hasStock: false,
14547                stock: 0,
14548                sku: 'Unknown Variant',
14549                variant: null
14550            };
14551        }
14552        
14553        // For pre-order products, check preorder_remaining instead of stock
14554        const isPreorder = window.isPreorderProduct || false;
14555        let hasStock = false;
14556        let availableStock = 0;
14557        
14558        if (isPreorder && 'preorder_remaining' in variant && variant.preorder_remaining !== null) {
14559            availableStock = parseInt(variant.preorder_remaining, 10);
14560            if (isNaN(availableStock)) availableStock = 0;
14561            hasStock = availableStock > 0;
14562        } else {
14563            availableStock = variant.stock || 0;
14564            hasStock = availableStock > 0;
14565        }
14566        
14567        return {
14568            hasStock: hasStock,
14569            stock: availableStock,
14570            sku: variant.sku,
14571            variant: variant
14572        };
14573    }
14574
14575    /**
14576     * Cross-browser AJAX helper
14577     */
14578    function makeRequest(url, options) {
14579        return new Promise((resolve, reject) => {
14580            // Try fetch first (modern browsers)
14581            if (typeof fetch !== 'undefined') {
14582                const fetchOptions = {
14583                    method: options.method || 'POST',
14584                    headers: {
14585                        'Content-Type': 'application/x-www-form-urlencoded',
14586                    },
14587                    body: options.data ? new URLSearchParams(options.data).toString() : null
14588                };
14589
14590                fetch(url, fetchOptions)
14591                    .then(response => {
14592                        if (!response.ok) {
14593                            throw new Error(`HTTP ${response.status}`);
14594                        }
14595                        return response.json();
14596                    })
14597                    .then(resolve)
14598                    .catch(reject);
14599            } else {
14600                // Fallback to XMLHttpRequest
14601                const xhr = new XMLHttpRequest();
14602                xhr.open(options.method || 'POST', url);
14603                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
14604                
14605                xhr.onload = function() {
14606                    if (xhr.status >= 200 && xhr.status < 300) {
14607                        try {
14608                            resolve(JSON.parse(xhr.responseText));
14609                        } catch (e) {
14610                            reject(new Error('Invalid JSON response'));
14611                        }
14612                    } else {
14613                        reject(new Error(`HTTP ${xhr.status}`));
14614                    }
14615                };
14616                
14617                xhr.onerror = function() {
14618                    reject(new Error('Network error'));
14619                };
14620                
14621                const data = options.data ? new URLSearchParams(options.data).toString() : null;
14622                xhr.send(data);
14623            }
14624        });
14625    }
14626
14627    /**
14628     * Check stock via AJAX
14629     */
14630    function checkStock(productId, variantId, qty) {
14631        return makeRequest(config.apiEndpoint, {
14632            method: 'POST',
14633            data: {
14634                product_id: productId,
14635                variant_id: variantId || 0,
14636                qty: qty || 1
14637            }
14638        });
14639    }
14640
14641    /**
14642     * Create and show stock alert modal using modal.css structure
14643     */
14644    function showStockModal(stockInfo, action = 'add to cart') {
14645        let title, message, type, iconClass;
14646
14647        // Handle both string and object formats
14648        if (typeof stockInfo === 'string') {
14649            title = 'Stok Tidak Tersedia';
14650            message = stockInfo;
14651            type = 'warning';
14652            iconClass = 'warning';
14653        } else if (stockInfo && typeof stockInfo === 'object') {
14654            if (!stockInfo.is_active) {
14655                title = 'Produk Tidak Tersedia';
14656                message = 'Maaf, produk ini sudah tidak aktif atau telah dihapus dari katalog.';
14657                type = 'danger';
14658                iconClass = 'danger';
14659            } else if (!stockInfo.is_in_stock) {
14660                title = 'Stok Habis';
14661                message = `<strong>${stockInfo.product_name || 'Produk ini'}</strong> sedang habis stok. Silakan pilih varian lain atau tunggu restock.`;
14662                if (stockInfo.sku) {
14663                    message += `<br><small style="color: #64748b;">SKU: ${stockInfo.sku}</small>`;
14664                }
14665                type = 'warning';
14666                iconClass = 'warning';
14667            } else if (!stockInfo.can_add_to_cart) {
14668                title = 'Stok Tidak Mencukupi';
14669                message = `Stok <strong>${stockInfo.product_name || 'produk'}</strong> tidak mencukupi.<br>`;
14670                message += `Tersedia: <strong>${stockInfo.available_stock}</strong>, diminta: <strong>${stockInfo.requested_qty}</strong><br>`;
14671                message += `Silakan kurangi jumlah atau pilih varian lain.`;
14672                type = 'warning';
14673                iconClass = 'warning';
14674            } else {
14675                title = 'Stok Tidak Tersedia';
14676                message = stockInfo.message || 'Produk ini tidak dapat ditambahkan ke keranjang saat ini.';
14677                type = 'warning';
14678                iconClass = 'warning';
14679            }
14680        } else {
14681            title = 'Stok Tidak Tersedia';
14682            message = 'Produk ini tidak dapat ditambahkan ke keranjang saat ini.';
14683            type = 'warning';
14684            iconClass = 'warning';
14685        }
14686
14687        // Create modal HTML using modal.css structure
14688        const modalHTML = `
14689            <div class="modal-overlay" id="stock-modal-overlay">
14690                <div class="modal-dialog">
14691                    <div class="modal-header">
14692                        <div class="modal-icon ${iconClass}">
14693                            ${getIconSVG(iconClass)}
14694                        </div>
14695                        <h3 class="modal-title">${title}</h3>
14696                    </div>
14697                    <div class="modal-body">
14698                        <p class="modal-message">${message}</p>
14699                        <div class="modal-actions">
14700                            <button type="button" class="modal-btn modal-btn-secondary" data-action="close">
14701                                Tutup
14702                            </button>
14703                            <button type="button" class="modal-btn modal-btn-primary" data-action="reload">
14704                                Muat Ulang
14705                            </button>
14706                        </div>
14707                    </div>
14708                </div>
14709            </div>
14710        `;
14711
14712        // Remove existing modal if any
14713        const existingModal = document.getElementById('stock-modal-overlay');
14714        if (existingModal) {
14715            existingModal.remove();
14716        }
14717
14718        // Add modal to body
14719        document.body.insertAdjacentHTML('beforeend', modalHTML);
14720        
14721        // Show modal with animation
14722        const modal = document.getElementById('stock-modal-overlay');
14723        setTimeout(() => {
14724            modal.classList.add('show');
14725        }, 10);
14726
14727        // Add event listeners to modal buttons
14728        const closeBtn = modal.querySelector('[data-action="close"]');
14729        const reloadBtn = modal.querySelector('[data-action="reload"]');
14730        
14731        if (closeBtn) {
14732            addEventListener(closeBtn, 'click', closeStockModal);
14733        }
14734        
14735        if (reloadBtn) {
14736            addEventListener(reloadBtn, 'click', function() {
14737                closeStockModal();
14738                window.location.reload();
14739            });
14740        }
14741
14742        // Close modal when clicking overlay
14743        addEventListener(modal, 'click', function(event) {
14744            if (event.target === modal) {
14745                closeStockModal();
14746            }
14747        });
14748
14749        // Auto-close after 8 seconds
14750        setTimeout(() => {
14751            closeStockModal();
14752        }, 8000);
14753    }
14754
14755    /**
14756     * Get icon SVG based on type
14757     */
14758    function getIconSVG(type) {
14759        const icons = {
14760            danger: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>',
14761            warning: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
14761<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>',
14762            success: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>',
14763            info: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>'
14764        };
14765        return icons[type] || icons.info;
14766    }
14767
14768    /**
14769     * Close stock modal
14770     */
14771    function closeStockModal() {
14772        const modal = document.getElementById('stock-modal-overlay');
14773        if (modal) {
14774            modal.classList.remove('show');
14775            setTimeout(() => {
14776                modal.remove();
14777            }, 200);
14778        }
14779    }
14780
14781    // Make closeStockModal globally available
14782    if (typeof window !== 'undefined') {
14783        window.closeStockModal = closeStockModal;
14784    }
14785
14786    /**
14787     * Get element data attribute with fallback
14788     */
14789    function getDataAttribute(element, attr) {
14790        if (element.dataset) {
14791            return element.dataset[attr];
14792        } else {
14793            return element.getAttribute('data-' + attr);
14794        }
14795    }
14796
14797    /**
14798     * Validate stock before cart action
14799     */
14800    async function validateBeforeCart(productId, variantId, qty, action = 'add') {
14801        try {
14802            // SMART VALIDATION: For simple products, check if we already have cached stock info
14803            // If buttons are enabled and cached info shows stock available, skip API validation
14804            if (window._currentStockInfo && window._currentStockInfo.can_add_to_cart) {
14805        
14806                return true;
14807            }
14808            
14809            // For variant products or when cached info is not available, do API validation
14810            const stockInfo = await checkStock(productId, variantId, qty);
14811            
14812            if (!stockInfo.ok) {
14813                showStockModal({ 
14814                    message: stockInfo.error || 'Gagal mengecek stok',
14815                    is_active: false 
14816                }, action);
14817                return false;
14818            }
14819
14820            if (!stockInfo.can_add_to_cart) {
14821                showStockModal(stockInfo, action);
14822                return false;
14823            }
14824
14825            return true; // Stock available, proceed
14826
14827        } catch (error) {
14828            console.error('Stock validation error:', error);
14829            showStockModal({ 
14830                message: 'Terjadi kesalahan saat mengecek stok. Silakan coba lagi.',
14831                is_active: false 
14832            }, action);
14833            return false;
14834        }
14835    }
14836
14837    /**
14838     * Handle button click with stock validation
14839     */
14840    function handleButtonClick(event, action) {
14841        const button = event.target;
14842        
14843        
14844        // FIRST CHECK: Is button marked as stock-blocked? (Anti-tampering)
14845        if (button.hasAttribute('data-stock-blocked')) {
14846            event.preventDefault();
14847            event.stopImmediatePropagation();
14848            
14849            const reason = button.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
14850            showStockModal(reason);
14851            
14852            // Show security warning
14853            if (window.showToast) {
14854                window.showToast({
14855                    type: 'warning',
14856                    title: 'Aksi Tidak Diizinkan',
14857                    message: 'Produk ini sedang habis stok. Silakan pilih varian lain.',
14858                    duration: 3000
14859                });
14860            }
14861            return false;
14862        }
14863        
14864        // SECOND CHECK: Real-time stock validation against cached stock info
14865        if (window._currentStockInfo && !window._currentStockInfo.can_add_to_cart) {
14866            event.preventDefault();
14867            event.stopImmediatePropagation();
14868            
14869            showStockModal(window._currentStockInfo.message || 'Stok tidak tersedia');
14870            
14871            // Re-enforce button disabling
14872            button.disabled = true;
14873            button.style.opacity = '0.5';
14874            button.style.cursor = 'not-allowed';
14875            button.setAttribute('data-stock-blocked', 'true');
14876            
14877            return false;
14878        }
14879        
14880        // Skip if already processing
14881        if (button.classList.contains(config.loadingClass)) {
14882            event.preventDefault();
14883            return false;
14884        }
14885
14886        // Get product data from button attributes or form
14887        let productId = parseInt(getDataAttribute(button, 'productId') || getDataAttribute(button, 'product-id') || 0);
14888        let variantId = parseInt(getDataAttribute(button, 'variantId') || getDataAttribute(button, 'variant-id') || 0);
14889        let qty = parseInt(getDataAttribute(button, 'qty') || 1);
14890
14891        // Try to get from global variables (set by product detail page)
14892        if (productId <= 0 && typeof window.PD_ID !== 'undefined') {
14893            productId = parseInt(window.PD_ID || 0);
14894        }
14895
14896        // Try to get from parent form if not in button
14897        if (productId <= 0) {
14898            const form = button.closest ? button.closest('form') : null;
14899            if (form) {
14900                const pidAttr = form.getAttribute('data-pid');
14901                const pidInput = form.querySelector('[name="product_id"]');
14902                const vidInput = form.querySelector('[name="variant_id"]');
14903                const qtyInput = form.querySelector('[name="qty"]');
14904                
14905                if (pidAttr) productId = parseInt(pidAttr);
14906                if (pidInput) productId = parseInt(pidInput.value || 0);
14907                if (vidInput) variantId = parseInt(vidInput.value || 0);
14908                if (qtyInput) qty = parseInt(qtyInput.value || 1);
14909            }
14910        }
14911
14912        // CRITICAL: Always get current variant from selector (overrides button attributes)
14913        // This ensures we validate against the currently selected variant, not cached button data
14914        const variantSelector = document.getElementById('pd-variant-id');
14915        if (variantSelector) {
14916            const currentVariantId = parseInt(variantSelector.value || 0);
14917            if (currentVariantId > 0) {
14918                variantId = currentVariantId;
14919            }
14920        }
14921
14922
14923        if (productId <= 0) {
14924            return true; // Let default handler deal with it
14925        }
14926
14927        // Prevent default and validate stock
14928        event.preventDefault();
14929        event.stopPropagation();
14930        
14931        // Show loading state
14932        button.classList.add(config.loadingClass);
14933        const originalDisabled = button.disabled;
14934        button.disabled = true;
14935        
14936        
14937        validateBeforeCart(productId, variantId, qty, action)
14938            .then(isValid => {
14939                if (isValid) {
14940                    // Stock OK, proceed with original action
14941                    if (action === 'cart' && typeof window.addToCart === 'function') {
14942                        window.addToCart(productId, qty, variantId);
14943                    } else if (action === 'buy' && typeof window.buyNow === 'function') {
14944                        // Call buyNow from pd-buynow.js without parameters (it gets data internally)
14945                        window.buyNow();
14946                    } else {
14947                        // Add fallback cart/buy logic here if needed
14948                    }
14949                }
14950            })
14951            .catch(error => {
14952            })
14953            .finally(() => {
14954                // Remove loading state
14955                button.classList.remove(config.loadingClass);
14956                button.disabled = originalDisabled;
14957            });
14958
14959        return false;
14960    }
14961
14962    /**
14963     * Handle mobile button click with stock validation
14964     */
14965    function handleMobileButtonClick(event, action) {
14966        const button = event.target;
14967        
14968        
14969        // FIRST CHECK: Is button marked as stock-blocked? (Anti-tampering)
14970        if (button.hasAttribute('data-stock-blocked')) {
14971            event.preventDefault();
14972            event.stopImmediatePropagation();
14973            
14974            const reason = button.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
14975            showStockModal(reason);
14976            
14977            // Show security warning
14978            if (window.showToast) {
14979                window.showToast({
14980                    type: 'warning',
14981                    title: 'Aksi Tidak Diizinkan',
14982                    message: 'Produk ini sedang habis stok. Silakan pilih varian lain.',
14983                    duration: 3000
14984                });
14985            }
14986            return false;
14987        }
14988        
14989        // SECOND CHECK: Real-time stock validation against cached stock info
14990        if (window._currentStockInfo && !window._currentStockInfo.can_add_to_cart) {
14991            event.preventDefault();
14992            event.stopImmediatePropagation();
14993            
14994            showStockModal(window._currentStockInfo.message || 'Stok tidak tersedia');
14995            
14996            // Re-enforce button disabling
14997            button.disabled = true;
14998            button.style.opacity = '0.5';
14999            button.style.cursor = 'not-allowed';
15000            button.setAttribute('data-stock-blocked', 'true');
15001            
15002            return false;
15003        }
15004        
15005        // Skip if already processing
15006        if (button.classList.contains(config.loadingClass)) {
15007            event.preventDefault();
15008            return false;
15009        }
15010
15011        // Get product data from mobile FAB container
15012        let productId = 0;
15013        let variantId = 0;
15014        let qty = 1;
15015
15016        // Try to get from mobile FAB data-pid
15017        const mobileFab = document.getElementById('pd-mobile-fab');
15018        if (mobileFab) {
15019            const pidAttr = getDataAttribute(mobileFab, 'pid');
15020            if (pidAttr) productId = parseInt(pidAttr);
15021        }
15022
15023        // Try to get from global variables (set by product detail page)
15024        if (productId <= 0 && typeof window.PD_ID !== 'undefined') {
15025            productId = parseInt(window.PD_ID || 0);
15026        }
15027
15028        // CRITICAL: Always get current variant from selector (real-time)
15029        // This ensures we validate against the currently selected variant
15030        const variantSelector = document.getElementById('pd-variant-id');
15031        if (variantSelector) {
15032            variantId = parseInt(variantSelector.value || 0);
15033        }
15034
15035        // Try to get quantity from quantity input
15036        const qtySelector = document.querySelector('[name="qty"]');
15037        if (qtySelector) {
15038            qty = parseInt(qtySelector.value || 1);
15039        }
15040        
15041        // Ensure minimum quantity is 1
15042        qty = Math.max(1, qty);
15043
15044
15045        if (productId <= 0) {
15046            return true; // Let default handler deal with it
15047        }
15048
15049        // Event already prevented at interceptor level, just proceed with validation
15050        
15051        // Show loading state
15052        button.classList.add(config.loadingClass);
15053        const originalDisabled = button.disabled;
15054        const originalText = button.textContent;
15055        button.disabled = true;
15056        button.textContent = action === 'cart' ? 'Validasi...' : 'Validasi...';
15057        
15058        
15059        // PREEMPTIVE BLOCKING: Set blocking flags BEFORE validation to prevent race conditions
15060        if (window.addToCart && typeof window.addToCart === 'function') {
15061            window.addToCart._stockValidatorBlocked = true;
15062        }
15063        
15064        // Also preemptively block other cart functions
15065        const originalAddToCartSubmit = window.addToCartSubmit;
15066        const originalPdAddToCartSubmit = window._pdAddToCartSubmit;
15067        
15068        // Temporarily block ALL cart functions during validation
15069        window.addToCartSubmit = (...args) => {
15070            return Promise.resolve(false);
15071        };
15072        
15073        if (originalPdAddToCartSubmit) {
15074            window._pdAddToCartSubmit = (...args) => {
15075                return Promise.resolve(false);
15076            };
15077        }
15078        
15079        validateBeforeCart(productId, variantId, qty, action)
15080            .then(isValid => {
15081                if (isValid) {
15082                    // Stock OK, proceed with original action
15083                    
15084                    if (action === 'cart') {
15085                        // For mobile cart, we need to recreate the original behavior
15086                        // Strategy: Try multiple methods to ensure cart add + toast success
15087                        
15088                        
15089                        let cartAddSuccess = false;
15090                        let errorMessage = '';
15091                        
15092                        // Method 1: Try original addToCart function
15093                        const originalAddToCart = window.addToCart && window.addToCart._original;
15094                        if (originalAddToCart && !cartAddSuccess) {
15095                            try {
15096                                const result = originalAddToCart(productId, qty, variantId);
15097                                
15098                                // Handle both sync and async returns
15099                                if (result && typeof result.then === 'function') {
15100                                    result.then(() => {
15101                                        cartAddSuccess = true;
15102                                    }).catch(err => {
15103                                        errorMessage = err.message || 'addToCart failed';
15104                                    });
15105                                } else {
15106                                    cartAddSuccess = true;
15107                                }
15108                            } catch (error) {
15109                                errorMessage = error.message || 'addToCart exception';
15110                            }
15111                        }
15112                        
15113                        // Method 2: Try pd-addtocart.js function (backup method)
15114                        if (typeof window._pdAddToCartSubmit === 'function' && !cartAddSuccess) {
15115                            try {
15116                                
15117                                // Create fake form for pd-addtocart.js
15118                                const fakeForm = document.createElement('form');
15119                                const qtyInput = document.createElement('input');
15120                                qtyInput.name = 'qty';
15121                                qtyInput.value = qty;
15122                                fakeForm.appendChild(qtyInput);
15123                                
15124                                // Set variant selector value for pd-addtocart to read
15125                                const variantSelector = document.getElementById('pd-variant-id');
15126                                if (variantSelector) {
15127                                    variantSelector.value = variantId;
15128                                }
15129                                
15130                                const fakeEvent = { 
15131                                    preventDefault: () => {},
15132                                    target: fakeForm
15133                                };
15134                                
15135                                // Call pd-addtocart.js function (this should handle toast internally)
15136                                const result = window._pdAddToCartSubmit(fakeEvent, productId);
15137                                if (result && typeof result.then === 'function') {
15138                                    result.then(() => {
15139                                        cartAddSuccess = true;
15140                                    });
15141                                } else {
15142                                    cartAddSuccess = true;
15143                                }
15144                            } catch (error) {
15145                            }
15146                        }
15147                        
15148                        // Method 3: Direct addToCart call (fallback)
15149                        if (typeof window.addToCart === 'function' && !cartAddSuccess) {
15150                            try {
15151                                const result = window.addToCart(productId, qty, variantId);
15152                                if (result && typeof result.then === 'function') {
15153                                    result.then(() => {
15154                                        cartAddSuccess = true;
15155                                    });
15156                                } else {
15157                                    cartAddSuccess = true;
15158                                }
15159                            } catch (error) {
15160                            }
15161                        }
15162                        
15163                        // Always show success toast for mobile (regardless of which method worked)
15164                        // This ensures user gets feedback for successful cart addition
15165                        setTimeout(() => {
15166                            if (window.showToast) {
15167                                
15168                                // Get product details for toast
15169                                const getVariantSummaryText = () => {
15170                                    const vs = document.getElementById('pd-variant-summary');
15171                                    if (!vs) return '';
15172                                    const items = Array.from(vs.querySelectorAll('.vs-item strong'))
15173                                        .map(el => el.textContent.trim())
15174                                        .filter(Boolean);
15175                                    return items.length ? items.join(' / ') : '';
15176                                };
15177                                
15178                                const escapeHtml = (s) => String(s||'').replace(/[&<>"']/g, m=>({
15179                                    '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'
15180                                }[m]));
15181                                
15182                                const variantText = getVariantSummaryText();
15183                                const skuText = (function(){ 
15184                                    const el = document.getElementById('pd-sku'); 
15185                                    return el ? el.textContent.trim() : (window.PD_SKU || 'N/A'); 
15186                                })();
15187                                const productName = window.PD_NAME || 'Product';
15188                                
15189                                window.showToast({
15190                                    type: 'success',
15191                                    title: 'Berhasil ditambahkan ke Keranjang',
15192                                    message: `<strong>${escapeHtml(productName)}</strong>${variantText ? ` · <em>${escapeHtml(variantText)}</em>` : ''}<br>SKU: ${escapeHtml(skuText)}
15192 · Qty: ${qty}`,
15193                                    duration: 5000,
15194                                    actions: [
15195                                        { 
15196                                            label: 'Lihat Keranjang', 
15197                                            onClick: () => { window.location.href = '/keranjang'; }
15198                                        },
15199                                        { 
15200                                            label: 'Lanjut Belanja', 
15201                                            variant: 'alt', 
15202                                            onClick: () => {} 
15203                                        }
15204                                    ]
15205                                });
15206                            } else {
15207                            }
15208                        }, 100); // Small delay to ensure cart operation completes first
15209                        
15210                    } else if (action === 'buy') {
15211                        // For mobile buy, call buyNow
15212                        if (typeof window.buyNow === 'function') {
15213                            window.buyNow();
15214                        }
15215                    }
15216                } else {
15217                    // Stock validation FAILED - absolutely NO cart function calls allowed
15218                    
15219                    // DO NOT CALL ANY CART FUNCTIONS - only show modal (already handled by validateBeforeCart)
15220                }
15221                
15222                // Always restore functions after validation (success or failure)
15223                setTimeout(() => {
15224                    // Remove blocking flag
15225                    if (window.addToCart && typeof window.addToCart === 'function') {
15226                        window.addToCart._stockValidatorBlocked = false;
15227                    }
15228                    
15229                    // Restore other functions
15230                    if (originalAddToCartSubmit && typeof originalAddToCartSubmit === 'function') {
15231                        window.addToCartSubmit = originalAddToCartSubmit;
15232                    }
15233                    if (originalPdAddToCartSubmit && typeof originalPdAddToCartSubmit === 'function') {
15234                        window._pdAddToCartSubmit = originalPdAddToCartSubmit;
15235                    }
15236                    
15237                }, 1000); // Shorter timeout since we want to restore quickly after success
15238            })
15239            .catch(error => {
15240                // On error, also block the action
15241            })
15242            .finally(() => {
15243                // Remove loading state
15244                button.classList.remove(config.loadingClass);
15245                button.disabled = originalDisabled;
15246                button.textContent = originalText;
15247            });
15248
15249        return false;
15250    }
15251
15252    /**
15253     * Intercept cart buttons
15254     */
15255    function interceptCartButtons() {
15256
15257        
15258        // CRITICAL: Remove existing mobile cart handlers from product-detail.php
15259        setTimeout(() => {
15260            const mobileCartBtn = document.getElementById('mf-cart');
15261            if (mobileCartBtn) {
15262                // Clone the button to remove ALL existing event listeners
15263                const newBtn = mobileCartBtn.cloneNode(true);
15264                mobileCartBtn.parentNode.replaceChild(newBtn, mobileCartBtn);
15265
15266                
15267                // Add our own handler to the new button
15268                newBtn.addEventListener('click', function(event) {
15269                    event.preventDefault();
15270                    event.stopPropagation();
15271                    event.stopImmediatePropagation();
15272                    handleMobileButtonClick(event, 'cart');
15273                    return false;
15274                }, true);
15275            }
15276        }, 50);
15277        
15278        // Note: Add to cart buttons are now handled via form submission override (addToCartSubmit)
15279        // This provides better integration with existing pd-addtocart.js toast system
15280
15281        // Buy now buttons - updated selectors (desktop + mobile)
15282        addEventListener(document, 'click', function(event) {
15283            const target = event.target;
15284            
15285            // Desktop buy buttons
15286            if (target.classList.contains('btn-buy') ||           // Product detail buy button
15287                target.classList.contains('btn-buy-now') || 
15288                target.classList.contains('buy-now-btn') || 
15289                target.id === 'btn-buy' ||                        // Specific ID match
15290                getDataAttribute(target, 'action') === 'buy-now' ||
15291                getDataAttribute(target, 'action') === 'buy') {
15292                
15293                handleButtonClick(event, 'buy');
15294            }
15295            
15296            // Mobile buy buttons
15297            else if (target.classList.contains('mf-buy') ||       // Mobile fab buy button
15298                     target.id === 'mf-buy') {                   // Mobile buy button ID
15299                
15300                handleMobileButtonClick(event, 'buy');
15301            }
15302            
15303            // Mobile cart buttons - ENHANCED BLOCKING
15304            else if (target.classList.contains('mf-cart') ||      // Mobile fab cart button
15305                     target.id === 'mf-cart') {                  // Mobile cart button ID
15306                
15307                
15308                // Immediately prevent all event propagation
15309                event.preventDefault();
15310                event.stopPropagation();
15311                event.stopImmediatePropagation();
15312                
15313                handleMobileButtonClick(event, 'cart');
15314                
15315                // Return false to ensure no other handlers run
15316                return false;
15317            }
15318        }, true); // Use capturing phase to ensure we intercept before other handlers
15319        
15320        // Additional protection: Override addToCart function to add mobile button state checking
15321        const originalAddToCart = typeof window.addToCart === 'function' ? window.addToCart : null;
15322        if (originalAddToCart) {
15323            window.addToCart = async function(...args) {
15324                // Check if this call is coming from a mobile button that's currently being validated
15325                const mobileCartBtn = document.getElementById('mf-cart');
15326                if (mobileCartBtn && mobileCartBtn.classList.contains('loading-stock')) {
15327                    return Promise.reject(new Error('Mobile validation in progress - cart action blocked'));
15328                }
15329                
15330                // Check if functions are temporarily blocked (during stock failure)
15331                if (window.addToCart._stockValidatorBlocked) {
15332                    return Promise.reject(new Error('Stock validation failed - cart action blocked'));
15333                }
15334                
15335                // Allow normal calls
15336                return originalAddToCart.apply(this, args);
15337            };
15338            
15339            // Store reference to original for restoration
15340            window.addToCart._original = originalAddToCart;
15341        }
15342    }
15343
15344    /**
15345     * Setup global click interceptor for security
15346     * This catches ALL clicks on cart/buy buttons and validates stock BEFORE any other handler
15347     */
15348    function setupGlobalClickInterceptor() {
15349        // Add capture-phase listener to catch clicks before other handlers
15350        document.addEventListener('click', function(event) {
15351            const target = event.target;
15352            
15353            // Check if this is a cart/buy button
15354            const isCartButton = target.matches('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart') ||
15355                                target.closest('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart');
15356            const isBuyButton = target.matches('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy') ||
15357                               target.closest('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy');
15358                               
15359            if (!isCartButton && !isBuyButton) {
15360                return; // Not our target, allow normal processing
15361            }
15362            
15363            const button = isCartButton ? 
15364                (target.matches('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart') ? target : target.closest('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart')) :
15365                (target.matches('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy') ? target : target.closest('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy'));
15366            
15367            // IMMEDIATE BLOCK: Prevent any processing first
15368            event.preventDefault();
15369            event.stopImmediatePropagation();
15370            
15371            // SECURITY CHECK 1: Is button marked as stock-blocked?
15372            if (button && button.hasAttribute('data-stock-blocked')) {
15373                
15374                const reason = button.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
15375                showStockModal(reason);
15376                
15377                // Show security alert
15378                if (window.showToast) {
15379                    window.showToast({
15380                        type: 'error',
15381                        title: 'Aksi Diblokir',
15382                        message: 'Produk ini sedang habis stok dan tidak dapat ditambahkan ke keranjang.',
15383                        duration: 4000
15384                    });
15385                }
15386                
15387                return false;
15388            }
15389            
15390            // SECURITY CHECK 2: Real-time validation against cached stock info
15391            if (window._currentStockInfo && !window._currentStockInfo.can_add_to_cart) {
15392                
15393                showStockModal(window._currentStockInfo.message || 'Stok tidak tersedia');
15394                
15395                // Re-enforce button disabling
15396                if (button) {
15397                    button.disabled = true;
15398                    button.style.opacity = '0.5';
15399                    button.style.cursor = 'not-allowed';
15400                    button.setAttribute('data-stock-blocked', 'true');
15401                    button.setAttribute('data-stock-reason', window._currentStockInfo.message || 'Stok tidak tersedia');
15402                }
15403                
15404                return false;
15405            }
15406            
15407            // SECURITY CHECK 3: Smart client-side validation
15408            const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15409            const variantSelector = document.getElementById('pd-variant-id');
15410            const variantId = variantSelector ? parseInt(variantSelector.value || 0) : 0;
15411            
15412            if (productId > 0) {
15413                // SMART VALIDATION: If we already have cached stock info showing stock is available,
15414                // and buttons are enabled, proceed immediately without re-validation
15415                if (window._currentStockInfo && window._currentStockInfo.can_add_to_cart && 
15416                    button && !button.disabled && !button.hasAttribute('data-stock-blocked')) {
15417                    
15418                    // Allow the action to proceed immediately with toast
15419                    if (isCartButton) {
15420                        // Trigger cart action with proper toast handling
15421                        if (typeof window._pdAddToCartSubmit === 'function') {
15422                            // Use pd-addtocart.js function for proper toast handling
15423                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15424                            if (form) {
15425                                const fakeEvent = { 
15426                                    target: form,
15427                                    preventDefault: () => {}
15428                                };
15429                                window._pdAddToCartSubmit(fakeEvent, productId);
15430                            }
15431                        } else if (typeof window.addToCartSubmit === 'function') {
15432                            // Fallback to addToCartSubmit
15433                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15434                            if (form) {
15435                                const fakeEvent = { 
15436                                    target: form,
15437                                    preventDefault: () => {}
15438                                };
15439                                window.addToCartSubmit(fakeEvent, productId);
15440                            }
15441                        } else if (typeof window.addToCart === 'function') {
15442                            // Direct addToCart call
15443                            window.addToCart(productId, 1, variantId);
15444                        }
15445                    } else if (isBuyButton) {
15446                        // Trigger buy action
15447                        if (typeof window.buyNow === 'function') {
15448                            window.buyNow();
15449                        }
15450                    }
15451                    
15452                    return false; // Action completed
15453                }
15454                
15455                // Get stock info from embedded variant data (for variant products)
15456                const stockInfo = getStockFromEmbeddedData(variantId);
15457                
15458                if (stockInfo && stockInfo.hasStock) {
15459                    
15460                    // Update global stock cache
15461                    window._currentStockInfo = {
15462                        can_add_to_cart: true,
15463                        is_in_stock: true,
15464                        available_stock: stockInfo.stock,
15465                        message: 'Stok tersedia'
15466                    };
15467                    
15468                    // IMPORTANT: Check if button was explicitly blocked by variant logic (e.g., pre-order exhausted)
15469                    // If data-stock-blocked is set, respect it and don't enable the button
15470                    const isExplicitlyBlocked = button.getAttribute('data-stock-blocked') === 'true';
15471                    
15472                    if (!isExplicitlyBlocked) {
15473                        // Remove stock blocking if it was set and enable button
15474                        button.removeAttribute('data-stock-blocked');
15475                        button.removeAttribute('data-stock-reason');
15476                        button.disabled = false;
15477                        button.style.opacity = '';
15478                        button.style.cursor = '';
15479                        button.classList.remove(config.disabledClass);
15480                    } else {
15481                        // Button is blocked by variant logic, show reason
15482                        const reason = button.getAttribute('data-stock-reason') || 'Tidak tersedia';
15483                        console.log('Button blocked by variant logic:', reason);
15484                        e.preventDefault();
15485                        e.stopPropagation();
15486                        return false;
15487                    }
15488                    
15489                    // Allow the action to proceed immediately with toast
15490                    if (isCartButton) {
15491                        // Trigger cart action with proper toast handling
15492                        if (typeof window._pdAddToCartSubmit === 'function') {
15493                            // Use pd-addtocart.js function for proper toast handling
15494                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15495                            if (form) {
15496                                const fakeEvent = { 
15497                                    target: form,
15498                                    preventDefault: () => {}
15499                                };
15500                                window._pdAddToCartSubmit(fakeEvent, productId);
15501                            }
15502                        } else if (typeof window.addToCartSubmit === 'function') {
15503                            // Fallback to addToCartSubmit
15504                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15505                            if (form) {
15506                                const fakeEvent = { 
15507                                    target: form,
15508                                    preventDefault: () => {}
15509                                };
15510                                window.addToCartSubmit(fakeEvent, productId);
15511                            }
15512                        } else if (typeof window.addToCart === 'function') {
15513                            // Direct addToCart call
15514                            window.addToCart(productId, 1, variantId);
15515                        }
15516                    } else if (isBuyButton) {
15517                        // Trigger buy action
15518                        if (typeof window.buyNow === 'function') {
15519                            window.buyNow();
15520                        }
15521                    }
15522                } else if (stockInfo && !stockInfo.hasStock) {
15523                    
15524                    // Update global stock cache
15525                    window._currentStockInfo = {
15526                        can_add_to_cart: false,
15527                        is_in_stock: false,
15528                        available_stock: stockInfo.stock,
15529                        message: `Stok tidak tersedia${stockInfo.sku ? ` untuk ${stockInfo.sku}` : ''}`
15530                    };
15531                    
15532                    // Force block button
15533                    button.disabled = true;
15534                    button.style.opacity = '0.5';
15535                    button.style.cursor = 'not-allowed';
15536                    button.setAttribute('data-stock-blocked', 'true');
15537                    button.setAttribute('data-stock-reason', window._currentStockInfo.message);
15538                    
15539                    // Show modal with variant info
15540                    const modalMessage = stockInfo ? 
15541                        `Varian yang dipilih (${stockInfo.sku}) sedang habis stok. Silakan pilih varian lain.` :
15542                        'Produk ini sedang habis stok. Silakan pilih varian lain.';
15543                    
15544                    showStockModal(modalMessage);
15545                    
15546                    // Show security warning
15547                    if (window.showToast) {
15548                        window.showToast({
15549                            type: 'warning',
15550                            title: 'Stok Tidak Tersedia',
15551                            message: 'Varian yang dipilih sedang habis stok. Silakan pilih varian lain.',
15552                            duration: 3000
15553                        });
15554                    }
15555                } else {
15556                    // stockInfo is null (simple product) - this means embedded data is not available
15557                    // For simple products, if buttons are enabled, we should proceed (trust the API response from updateStockDisplay)
15558                    if (button && !button.disabled && !button.hasAttribute('data-stock-blocked')) {
15559                        
15560                        // Allow the action to proceed immediately with toast
15561                        if (isCartButton) {
15562                            // Trigger cart action with proper toast handling
15563                            if (typeof window._pdAddToCartSubmit === 'function') {
15564                                // Use pd-addtocart.js function for proper toast handling
15565                                const form = document.getElementById('pd-buy-form') || button.closest('form');
15566                                if (form) {
15567                                    const fakeEvent = { 
15568                                        target: form,
15569                                        preventDefault: () => {}
15570                                    };
15571                                    window._pdAddToCartSubmit(fakeEvent, productId);
15572                                }
15573                            } else if (typeof window.addToCartSubmit === 'function') {
15574                                // Fallback to addToCartSubmit
15575                                const form = document.getElementById('pd-buy-form') || button.closest('form');
15576                                if (form) {
15577                                    const fakeEvent = { 
15578                                        target: form,
15579                                        preventDefault: () => {}
15580                                    };
15581                                    window.addToCartSubmit(fakeEvent, productId);
15582                                }
15583                            } else if (typeof window.addToCart === 'function') {
15584                                // Direct addToCart call
15585                                window.addToCart(productId, 1, variantId);
15586                            }
15587                        } else if (isBuyButton) {
15588                            // Trigger buy action
15589                            if (typeof window.buyNow === 'function') {
15590                                window.buyNow();
15591                            }
15592                        }
15593                    } else {
15594                        // Button is disabled, don't allow action
15595                        showStockModal('Produk ini sedang tidak tersedia.');
15596                    }
15597                }
15598            } else {
15599                // No product ID, block action
15600                showStockModal('Data produk tidak valid.');
15601            }
15602            
15603            return false; // Always block at capture phase
15604            
15605        }, true); // Use capture phase to run BEFORE other listeners
15606        
15607
15608    }
15609
15610    /**
15611     * Initialize button data attributes from current page
15612     */
15613    function initializeButtonAttributes() {
15614        // Get product data from page globals or form
15615        let productId = 0, variantId = 0;
15616        
15617        // Try to get from global variables (set by PHP)
15618        if (window.PD_ID) productId = parseInt(window.PD_ID);
15619        
15620        // Try to get from form
15621        const form = document.getElementById('pd-buy-form');
15622        if (form) {
15623            const pidAttr = form.getAttribute('data-pid');
15624            if (pidAttr) productId = parseInt(pidAttr);
15625            
15626            const varInput = document.getElementById('pd-variant-id');
15627            if (varInput) variantId = parseInt(varInput.value || 0);
15628        }
15629        
15630        // Try to get from mobile FAB
15631        const mobileFab = document.getElementById('pd-mobile-fab');
15632        if (mobileFab && productId <= 0) {
15633            const pidAttr = getDataAttribute(mobileFab, 'pid');
15634            if (pidAttr) productId = parseInt(pidAttr);
15635        }
15636        
15637        // Set attributes on desktop buttons
15638        const desktopButtons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, #btn-add-cart, #btn-buy, #btn-buy-voucher');
15639        desktopButtons.forEach(button => {
15640            if (productId > 0) {
15641                button.setAttribute('data-product-id', productId);
15642                if (variantId > 0) {
15643                    button.setAttribute('data-variant-id', variantId);
15644                }
15645            }
15646        });
15647        
15648        // Set attributes on mobile buttons
15649        const mobileButtons = document.querySelectorAll('.mf-cart, .mf-buy, #mf-cart, #mf-buy');
15650        mobileButtons.forEach(button => {
15651            if (productId > 0) {
15652                button.setAttribute('data-product-id', productId);
15653                if (variantId > 0) {
15654                    button.setAttribute('data-variant-id', variantId);
15655                }
15656            }
15657        });
15658        
15659
15660        
15661        // Initial stock display update on page load
15662        if (productId > 0) {
15663            setTimeout(() => {
15664                updateStockDisplay(productId, variantId);
15665
15666            }, 100);
15667        }
15668    }
15669
15670    /**
15671     * Update button attributes when variant changes
15672     */
15673    function updateButtonAttributesForVariant(variantId) {
15674        const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15675        
15676        if (productId <= 0) return;
15677        
15678        // Update desktop buttons
15679        const desktopButtons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, #btn-add-cart, #btn-buy, #btn-buy-voucher');
15680        desktopButtons.forEach(button => {
15681            button.setAttribute('data-product-id', productId);
15682            if (variantId > 0) {
15683                button.setAttribute('data-variant-id', variantId);
15684            } else {
15685                button.removeAttribute('data-variant-id');
15686            }
15687        });
15688        
15689        // Update mobile buttons
15690        const mobileButtons = document.querySelectorAll('.mf-cart, .mf-buy, #mf-cart, #mf-buy');
15691        mobileButtons.forEach(button => {
15692            button.setAttribute('data-product-id', productId);
15693            if (variantId > 0) {
15694                button.setAttribute('data-variant-id', variantId);
15695            } else {
15696                button.removeAttribute('data-variant-id');
15697            }
15698        });
15699        
15700
15701    }
15702
15703    /**
15704     * Setup variant change integration with existing product-detail.php handlers
15705     */
15706    function setupVariantChangeIntegration() {
15707        // Strategy: Hook into existing applyVariant function instead of adding new listener
15708        // This prevents conflicts with existing variant change functionality (gallery, pricing, etc.)
15709        
15710        // Method 1: Hook into existing applyVariant function
15711        setTimeout(() => {
15712            if (typeof window.applyVariant === 'function') {
15713                const originalApplyVariant = window.applyVariant;
15714                window.applyVariant = function(v, focusImage) {
15715                    // Call original function first
15716                    const result = originalApplyVariant.call(this, v, focusImage);
15717                    
15718                // Then add our stock validator logic
15719                const variantId = v ? (v.id || v.ID || 0) : 0;
15720                
15721                // Update button attributes for stock validator
15722                updateButtonAttributesForVariant(variantId);
15723                
15724                // Update stock display using embedded data (instant)
15725                const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15726                if (productId > 0) {
15727                    updateStockDisplay(productId, variantId);
15728                }                    return result;
15729                };
15730                
15731
15732            } else {
15733
15734                setupVariantObserver();
15735            }
15736        }, 500);
15737        
15738        // Method 2: Direct observation of hidden input value changes (fallback)
15739        setTimeout(() => {
15740            setupVariantObserver();
15741        }, 1000);
15742    }
15743
15744    /**
15745     * Setup variant observer as fallback when applyVariant hook fail
15745s
15746     */
15747    function setupVariantObserver() {
15748        const variantInput = document.getElementById('pd-variant-id');
15749        if (variantInput) {
15750            let lastVariantId = parseInt(variantInput.value || '0');
15751            
15752            // Use MutationObserver to watch for value changes
15753            if (window.MutationObserver) {
15754                const observer = new MutationObserver((mutations) => {
15755                    mutations.forEach((mutation) => {
15756                        if (mutation.type === 'attributes' && mutation.attributeName === 'value') {
15757                            const newVariantId = parseInt(variantInput.value || '0');
15758                            if (newVariantId !== lastVariantId) {
15759                    
15760                                updateButtonAttributesForVariant(newVariantId);
15761                                
15762                                // Update stock display with embedded data
15763                                const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15764                                if (productId > 0) {
15765                                    updateStockDisplay(productId, newVariantId);
15766                                }
15767                                
15768                                lastVariantId = newVariantId;
15769                            }
15770                        }
15771                    });
15772                });
15773                
15774                observer.observe(variantInput, { 
15775                    attributes: true, 
15776                    attributeFilter: ['value'] 
15777                });
15778                
15779
15780            }
15781            
15782            // Additional polling as ultimate fallback
15783            setInterval(() => {
15784                const currentVariantId = parseInt(variantInput.value || '0');
15785                if (currentVariantId !== lastVariantId) {
15786        
15787                    updateButtonAttributesForVariant(currentVariantId);
15788                    
15789                    // Update stock display with embedded data
15790                    const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15791                    if (productId > 0) {
15792                        updateStockDisplay(productId, currentVariantId);
15793                    }
15794                    
15795                    lastVariantId = currentVariantId;
15796                }
15797            }, 1000);
15798            
15799
15800        }
15801    }
15802
15803    /**
15804     * Update stock display and enforce stock-based button blocking using embedded data
15805     */
15806    function updateStockDisplay(productId, variantId = 0) {
15807
15808        
15809        // Get stock info from embedded data (instant)
15810        const embeddedStockInfo = getStockFromEmbeddedData(variantId);
15811        
15812        let stockInfo;
15813        if (embeddedStockInfo) {
15814            // Convert embedded data to standard format
15815            stockInfo = {
15816                can_add_to_cart: embeddedStockInfo.hasStock,
15817                is_in_stock: embeddedStockInfo.hasStock,
15818                available_stock: embeddedStockInfo.stock,
15819                message: embeddedStockInfo.hasStock ? 'Stok tersedia' : `Stok habis${embeddedStockInfo.sku ? ` untuk ${embeddedStockInfo.sku}` : ''}`,
15820                product_name: window.PD_NAME || 'Produk',
15821                sku: embeddedStockInfo.sku
15822            };
15823            
15824
15825        } else {
15826            // Fallback to API call if embedded data not available
15827
15828            checkStock(productId, variantId, 1)
15829                .then(apiStockInfo => {
15830                    updateStockDisplayWithInfo(apiStockInfo, productId, variantId);
15831                })
15832                .catch(error => {
15833                    console.error('Failed to update stock display via API:', error);
15834                });
15835            return;
15836        }
15837        
15838        updateStockDisplayWithInfo(stockInfo, productId, variantId);
15839    }
15840
15841    /**
15842     * Update stock display with provided stock info
15843     */
15844    function updateStockDisplayWithInfo(stockInfo, productId, variantId) {
15845        // Store current stock status globally for blocking checks
15846        window._currentStockInfo = stockInfo;
15847        
15848        // Update stock indicators
15849        const stockIndicators = document.querySelectorAll('.stock-indicator');
15850        stockIndicators.forEach(indicator => {
15851            if (stockInfo.is_in_stock) {
15852                indicator.classList.add('in-stock');
15853                indicator.classList.remove('out-of-stock');
15854            } else {
15855                indicator.classList.add('out-of-stock');
15856                indicator.classList.remove('in-stock');
15857            }
15858        });
15859        
15860        // Update stock count
15861        const stockCounts = document.querySelectorAll('.stock-count');
15862        stockCounts.forEach(count => {
15863            count.textContent = stockInfo.available_stock || 0;
15864        });
15865        
15866        // CRITICAL: For digital products, DO NOT modify button states here
15867        // PHP already handles button disable/enable based on LICENSE_POOL count
15868        // Only physical products need JavaScript stock validation
15869        const isDigitalProduct = window.IS_DIGITAL === true || window.IS_DIGITAL === 'true';
15870        
15871        if (isDigitalProduct) {
15872            // For digital products: ONLY update global stock info, don't touch buttons
15873            // Let PHP-rendered disabled state remain authoritative
15874            console.log('[Stock Validator] Digital product detected - skipping button state modification');
15875            return; // Exit early, preserve PHP-set button states
15876        }
15877        
15878        // ENHANCED: Force disable buttons for out-of-stock with anti-tampering (PHYSICAL PRODUCTS ONLY)
15879        const allButtons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, .mf-cart, .mf-buy, #btn-add-cart, #btn-buy, #btn-buy-voucher, #mf-cart, #mf-buy');
15880        allButtons.forEach(button => {
15881            if (stockInfo.can_add_to_cart) {
15882                // STOCK AVAILABLE - ENABLE BUTTONS
15883                button.classList.remove(config.disabledClass);
15884                button.disabled = false;
15885                button.style.opacity = '';
15886                button.style.cursor = '';
15887                
15888                // CRITICAL: Remove stock blocking attributes when stock becomes available
15889                button.removeAttribute('data-stock-blocked');
15890                button.removeAttribute('data-stock-reason');
15891                
15892
15893            } else {
15894                // OUT OF STOCK - FORCE DISABLE WITH ANTI-TAMPERING
15895                button.classList.add(config.disabledClass);
15896                button.disabled = true;
15897                button.style.opacity = '0.5';
15898                button.style.cursor = 'not-allowed';
15899                
15900                // Mark as stock-blocked to prevent re-enabling
15901                button.setAttribute('data-stock-blocked', 'true');
15902                button.setAttribute('data-stock-reason', stockInfo.message || 'Stok tidak tersedia');
15903                
15904
15905            }
15906        });
15907        
15908        // Start aggressive button monitoring for out-of-stock variants
15909        if (!stockInfo.can_add_to_cart) {
15910            enforceStockDisabling(productId, variantId);
15911        }
15912        
15913
15914    }
15915
15916    /**
15917     * Aggressively enforce button disabling for out-of-stock items
15918     * CRITICAL: For digital products, DO NOT modify button states (PHP is authoritative)
15919     */
15920    function enforceStockDisabling(productId, variantId) {
15921        // CRITICAL: Skip enforcement for digital products - PHP handles button states
15922        const isDigitalProduct = window.IS_DIGITAL === true || window.IS_DIGITAL === 'true';
15923        if (isDigitalProduct) {
15924            console.log('[Stock Validator] Digital product - skipping button enforcement');
15925            return; // Exit early, let PHP-rendered disabled state remain
15926        }
15927        
15928        const monitorInterval = setInterval(() => {
15929            // Check if variant changed
15930            const currentVariantSelector = document.getElementById('pd-variant-id');
15931            const currentVariantId = currentVariantSelector ? parseInt(currentVariantSelector.value || 0) : 0;
15932            
15933            if (currentVariantId !== variantId) {
15934                // Variant changed, stop monitoring this variant
15935                clearInterval(monitorInterval);
15936
15937                return;
15938            }
15939            
15940            // Check current stock status using embedded data (real-time check)
15941            const currentStockInfo = getStockFromEmbeddedData(currentVariantId);
15942            
15943            if (currentStockInfo && !currentStockInfo.hasStock) {
15944                // Still out of stock - continue monitoring for tampering
15945                const buttons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, .mf-cart, .mf-buy, #btn-add-cart, #btn-buy, #btn-buy-voucher, #mf-cart, #mf-buy');
15946                buttons.forEach(button => {
15947                    // Check if button was manually enabled (inspect attack)
15948                    if (!button.disabled || button.style.opacity !== '0.5') {
15949                        
15950                        // Force re-disable
15951                        button.disabled = true;
15952                        button.style.opacity = '0.5';
15953                        button.style.cursor = 'not-allowed';
15954                        button.setAttribute('data-stock-blocked', 'true');
15955                        button.setAttribute('data-stock-reason', `Stok tidak tersedia${currentStockInfo.sku ? ` untuk ${currentStockInfo.sku}` : ''}`);
15956                        
15957                        // Show warning toast
15958                        if (window.showToast) {
15959                            window.showToast({
15960                                type: 'warning',
15961                                title: 'Aksi Tidak Diizinkan',
15962                                message: 'Produk ini sedang habis stok. Silakan pilih varian lain.',
15963                                duration: 3000
15964                            });
15965                        }
15966                    }
15967                });
15968            } else {
15969                // Stock available OR embedded data shows stock - stop monitoring
15970                clearInterval(monitorInterval);
15971
15972                // For PHYSICAL products only, enable buttons
15973                // For DIGITAL products, skip (PHP is authoritative)
15974                const isDigitalProduct = window.IS_DIGITAL === true || window.IS_DIGITAL === 'true';
15975                
15976                if (!isDigitalProduct && currentStockInfo && currentStockInfo.hasStock) {
15977                    const buttons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, .mf-cart, .mf-buy, #btn-add-cart, #btn-buy, #btn-buy-voucher, #mf-cart, #mf-buy');
15978                    buttons.forEach(button => {
15979                        button.disabled = false;
15980                        button.style.opacity = '';
15981                        button.style.cursor = '';
15982                        button.removeAttribute('data-stock-blocked');
15983                        button.removeAttribute('data-stock-reason');
15984                        button.classList.remove(config.disabledClass);
15985                    });
15986
15987                }
15988            }
15989        }, 500); // Check every 500ms for tampering attempts
15990        
15991        // Auto-stop monitoring after 30 seconds to prevent memory leaks
15992        setTimeout(() => {
15993            clearInterval(monitorInterval);
15994
15995        }, 30000);
15996    }
15997
15998    /**
15999     * Override addToCartSubmit function to add stock validation
16000     * This function now works with pd-addtocart.js integration
16001     */
16002    function overrideAddToCartSubmitFunction() {
16003        // Check if pd-addtocart.js is already loaded
16004        const pdAddToCartExists = typeof window.addToCartSubmit === 'function' && 
16005                                  window.addToCartSubmit.toString().includes('showToast');
16006        
16007        if (pdAddToCartExists) {
16008
16009            
16010            // Store the pd-addtocart.js function
16011            const pdAddToCartSubmit = window.addToCartSubmit;
16012            window._pdAddToCartSubmit = pdAddToCartSubmit;
16013            
16014            // Override with stock validation that calls pd-addtocart.js
16015            window.addToCartSubmit = async function(e, productId) {
16016                
16017                // Don't prevent default here - let pd-addtocart handle it if stock is valid
16018                
16019                try {
16020                    // Extract product data from form (same way as pd-addtocart.js)
16021                    const form = e.target;
16022                    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16023                    
16024                    // CRITICAL: Always get current variant from selector (real-time)
16025                    let variantId = 0;
16026                    const variantSelector = document.getElementById('pd-variant-id');
16027                    if (variantSelector) {
16028                        variantId = parseInt(variantSelector.value || '0', 10) || 0;
16029                    }
16030                    
16031                    if (!productId || productId <= 0) {
16032                        return pdAddToCartSubmit(e, productId);
16033                    }
16034
16035
16036                    // Validate stock before proceeding
16037                    const isValid = await validateBeforeCart(productId, variantId, qty, 'cart');
16038                    
16039                    if (isValid) {
16040                        // Stock OK, call pd-addtocart.js function (it will handle toast)
16041                        return pdAddToCartSubmit(e, productId);
16042                    } else {
16043                        // Prevent the form submission
16044                        e.preventDefault();
16045                        // Stock validation failed, modal already shown by validateBeforeCart
16046                        return false;
16047                    }
16048                    
16049                } catch (error) {
16050                    e.preventDefault();
16051                    // On error, show modal and don't proceed
16052                    showStockModal({ 
16053                        message: 'Terjadi kesalahan saat memvalidasi stok. Silakan coba lagi.',
16054                        is_active: false 
16055                    }, 'cart');
16056                    return false;
16057                }
16058            };
16059        } else {
16060            // Original logic for when pd-addtocart.js is not present
16061            const originalAddToCartSubmit = typeof window.addToCartSubmit === 'function' ? window.addToCartSubmit : null;
16062            
16063            if (originalAddToCartSubmit) {
16064                window._originalAddToCartSubmit = originalAddToCartSubmit;
16065            }
16066            
16067            // Override with stock validation
16068            window.addToCartSubmit = async function(e, productId) {
16069                
16070                e.preventDefault();
16071                
16072                try {
16073                    const form = e.target;
16074                    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16075                    
16076                    // CRITICAL: Always get current variant from selector (real-time)
16077                    let variantId = 0;
16078                    const variantSelector = document.getElementById('pd-variant-id');
16079                    if (variantSelector) {
16080                        variantId = parseInt(variantSelector.value || '0', 10) || 0;
16081                    }
16082                    
16083                    if (!productId || productId <= 0) {
16084                        if (originalAddToCartSubmit) return originalAddToCartSubmit(e, productId);
16085                        return false;
16086                    }
16087
16088
16089                    const isValid = await validateBeforeCart(productId, variantId, qty, 'cart');
16090                    
16091                    if (isValid) {
16092                        if (originalAddToCartSubmit) {
16093                            return originalAddToCartSubmit(e, productId);
16094                        } else {
16095                            if (typeof window.addToCart === 'function') {
16096                                await window.addToCart(productId, qty, variantId);
16097                            }
16098                        }
16099                    } else {
16100                    }
16101                    
16102                } catch (error) {
16103                    showStockModal({ 
16104                        message: 'Terjadi kesalahan saat memvalidasi stok. Silakan coba lagi.',
16105                        is_active: false 
16106                    }, 'cart');
16107                }
16108                
16109                return false;
16110            };
16111        }
16112
16113
16114    }
16115
16116    /**
16117     * Override buyNow function to add stock validation
16118     */
16119    function overrideBuyNowFunction() {
16120        // Store original buyNow function if it exists
16121        const originalBuyNow = typeof window.buyNow === 'function' ? window.buyNow : null;
16122        
16123        // Override with stock validation
16124        window.buyNow = async function(e) {
16125            
16126            try {
16127                // Get product data (same way as pd-buynow.js)
16128                const form = document.getElementById('pd-buy-form');
16129                if (!form) {
16130                    if (originalBuyNow) return originalBuyNow(e);
16131                    return;
16132                }
16133
16134                const productId = parseInt(form.dataset.pid || '0', 10);
16135                const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16136                
16137                // CRITICAL: Always get current variant from selector (real-time)
16138                let variantId = 0;
16139                const variantSelector = document.getElementById('pd-variant-id');
16140                if (variantSelector) {
16141                    variantId = parseInt(variantSelector.value || '0', 10) || 0;
16142                }
16143                
16144                if (!productId || productId <= 0) {
16145                    if (originalBuyNow) return originalBuyNow(e);
16146                    return;
16147                }
16148
16149
16150                // Validate stock before proceeding
16151                const isValid = await validateBeforeCart(productId, variantId, qty, 'buy');
16152                
16153                if (isValid) {
16154                    // Stock OK, call original buyNow function
16155                    if (originalBuyNow) {
16156                        return originalBuyNow(e);
16157                    } else {
16158                    }
16159                } else {
16160                    // Stock validation failed, modal already shown by validateBeforeCart
16161                }
16162                
16163            } catch (error) {
16164                // On error, show modal and don't proceed
16165                showStockModal({ 
16166                    message: 'Terjadi kesalahan saat memvalidasi stok. Silakan coba lagi.',
16167                    is_active: false 
16168                }, 'buy');
16169            }
16170        };
16171
16172
16173    }
16174
16175    /**
16176     * Initialize stock validation
16177     */
16178    function init() {
16179        // Wait for DOM ready
16180        if (document.readyState === 'loading') {
16181            addEventListener(document, 'DOMContentLoaded', function() {
16182        initializeButtonAttributes();
16183        setupVariantChangeIntegration(); // Setup variant change integration
16184        interceptCartButtons();
16185        setupGlobalClickInterceptor(); // Setup global click security interceptor                // Override functions after other scripts load
16186                // Use multiple timeouts to catch late-loading scripts like pd-addtocart.js
16187                setTimeout(() => {
16188                    overrideAddToCartSubmitFunction();
16189                    overrideBuyNowFunction();
16190                }, 100);
16191                
16192                setTimeout(() => {
16193                    // Re-check and re-override if pd-addtocart.js loaded later
16194                    overrideAddToCartSubmitFunction();
16195                }, 500);
16196                
16197                setTimeout(() => {
16198                    // Final check for very late loading scripts
16199                    overrideAddToCartSubmitFunction();
16200                }, 1000);
16201                
16202
16203            });
16204        } else {
16205            initializeButtonAttributes();
16206            setupVariantChangeIntegration(); // Setup variant change integration
16207            interceptCartButtons();
16208            setupGlobalClickInterceptor(); // Setup global click security interceptor
16209            
16210            // Override functions after other scripts load
16211            // Use multiple timeouts to catch late-loading scripts like pd-addtocart.js
16212            setTimeout(() => {
16213                overrideAddToCartSubmitFunction();
16214                overrideBuyNowFunction();
16215            }, 100);
16216            
16217            setTimeout(() => {
16218                // Re-check and re-override if pd-addtocart.js loaded later
16219                overrideAddToCartSubmitFunction();
16220            }, 500);
16221            
16222            setTimeout(() => {
16223                // Final check for very late loading scripts
16224                overrideAddToCartSubmitFunction();
16225            }, 1000);
16226            
16227
16228        }
16229    }
16230
16231    // Public API
16232    return {
16233        init: init,
16234        checkStock: checkStock,
16235        validateBeforeCart: validateBeforeCart,
16236        updateStockDisplay: updateStockDisplay,
16237        showStockModal: showStockModal,
16238        config: config
16239    };
16240})();
16241
16242// Auto-initialize
16243if (typeof window !== 'undefined') {
16244    StockValidator.init();
16245}
16246
16247</script>
vendor: 1 bytes, line 16247
16247
16248<script data-inlined="pd-addtocart.js">
16249(function(){
16250  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
16251  function getVariantSummaryText(){
16252    const vs = document.getElementById('pd-variant-summary');
16253    if (!vs) return '';
16254    const items = Array.from(vs.querySelectorAll('.vs-item strong')).map(el => el.textContent.trim()).filter(Boolean);
16255    return items.length ? items.join(' / ') : '';
16256  }
16257  window.addToCartSubmit = async function(e, productId){
16258    e.preventDefault();
16259    try {
16260      const form = e.target;
16261      const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16262      const variantId = parseInt(document.getElementById('pd-variant-id')?.value || '0', 10) || 0;
16263      await (window.addToCart ? window.addToCart(productId, qty, variantId) : Promise.reject(new Error('addToCart tidak tersedia')));
16264
16265      if (window.PlazaAnalytics) {
16266        window.PlazaAnalytics.track('add_to_cart', { slug: String(window._pit_slug || '') });
16267      }
16268
16269      const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
16270      const sku  = (function(){ const el=document.getElementById('pd-sku'); return el?el.textContent.trim(): (window.PD_SKU||''); })();
16271      const variantText = getVariantSummaryText();
16272
16273      if (window.showToast) {
16274        window.showToast({
16275          type: 'success',
16276          title: 'Berhasil ditambahkan ke Keranjang',
16277          message: `<strong>${esc(name)}</strong>${variantText?` · <em>${esc(variantText)}</em>`:''}<br>SKU: ${esc(sku)} · Qty: ${qty}`,
16278          duration: 5000,
16279          actions: [
16280            { label: 'Lihat Keranjang', onClick: ()=> { window.location.href = '/keranjang'; } },
16281            { label: 'Lanjut Belanja', variant: 'alt', onClick: ()=>{} }
16282          ]
16283        });
16284      }
16285    } catch (err) {
16286      if (window.showToast) {
16287        window.showToast({ type:'warn', title:'Gagal menambahkan', message: String(err?.message||'Terjadi kesalahan'), duration: 4500 });
16288      } else {
16289        console.error(err);
16290      }
16291    }
16292    return false;
16293  };
16294})();
16295</script>
vendor: 1 bytes, line 16295
16295
16296<script data-inlined="pd-buynow.js">
16297(function(){
16298  
16299  // CSRF Token Helper
16300  function getCsrfToken() {
16301    return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
16302  }
16303  
16304  // Utility functions
16305  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
16306  
16307  function getVariantSummaryText(){
16308    const vs = document.getElementById('pd-variant-summary');
16309    if (!vs) return '';
16310    const items = Array.from(vs.querySelectorAll('.vs-item strong')).map(el => el.textContent.trim()).filter(Boolean);
16311    return items.length ? items.join(' / ') : '';
16312  }
16313
16314  function getCurrentProductData(){
16315    const form = document.getElementById('pd-buy-form');
16316    if (!form) return null;
16317
16318    const productId = parseInt(form.dataset.pid || '0', 10);
16319    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16320    const variantId = parseInt(document.getElementById('pd-variant-id')?.value || '0', 10) || 0;
16321    
16322    if (!productId || productId <= 0) {
16323      throw new Error('Product ID tidak valid');
16324    }
16325
16326    if (qty <= 0) {
16327      throw new Error('Quantity harus lebih dari 0');
16328    }
16329
16330    return { productId, qty, variantId };
16331  }
16332
16333  function createBuyNowPayload(productId, qty, variantId) {
16334    // Get product metadata
16335    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
16336    
16337    // Get correct price, SKU, and weight from selected variant data instead of DOM elements
16338    let price = 0;
16339    let sku = '';
16340    let weight = 0;
16341    let dimensions = null;
16342    
16343    if (variantId > 0 && typeof window.variants !== 'undefined' && Array.isArray(window.variants)) {
16344      // Find the selected variant in the variants array
16345      const selectedVariant = window.variants.find(v => v.id === variantId);
16346      if (selectedVariant) {
16347        price = selectedVariant.final || 0;
16348        sku = selectedVariant.sku || '';
16349        weight = selectedVariant.weight || 0;
16350        
16351        // Include dimensions if available
16352        if (selectedVariant.length_cm || selectedVariant.width_cm || selectedVariant.height_cm) {
16353          dimensions = {
16354            length_cm: selectedVariant.length_cm || 0,
16355            width_cm: selectedVariant.width_cm || 0,
16356            height_cm: selectedVariant.height_cm || 0
16357          };
16358        }
16359        
16360        // Debug info to verify fix is working
16361        if (window.console && window.console.debug) {
16362          console.debug('BuyNow: Using variant data', {
16363            variantId: variantId,
16364            price: price,
16365            sku: sku,
16366            weight: weight,
16367            dimensions: dimensions,
16368            variantData: selectedVariant
16369          });
16370        }
16371      }
16372    }
16373    
16374    // Fallback to DOM elements if variant data not found
16375    if (price === 0) {
16376      const priceTxt = document.getElementById('pd-price-current')?.textContent || '';
16377      price = parseInt(String(priceTxt).replace(/[^\d]/g,''), 10) || 0;
16378      
16379      if (window.console && window.console.debug) {
16380        console.debug('BuyNow: Using DOM price fallback', { 
16381          priceTxt: priceTxt, 
16382          price: price,
16383          variantId: variantId,
16384          reason: variantId > 0 ? 'variant_data_not_found' : 'no_variant_selected'
16385        });
16386      }
16387    }
16388    
16389    if (!sku) {
16390      const skuEl = document.getElementById('pd-sku');
16391      sku = skuEl ? skuEl.textContent.trim() : (window.PD_SKU || '');
16392    }
16393    
16394    const imgEl = document.getElementById('pd-main-img');
16395    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
16396
16397    const variantText = getVariantSummaryText();
16398    
16399    // Get pre-order data if product is pre-order
16400    const preorderData = window.PD_PREORDER_DATA || null;
16401    
16402    return {
16403      product_id: productId,
16404      qty: qty,
16405      variant_id: variantId,
16406      meta: {
16407        name,
16408        sku,
16409        price,
16410        image_url,
16411        variant_text: variantText,
16412        weight: weight,
16413        dimensions: dimensions
16414      },
16415      is_preorder: preorderData ? 'Y' : 'N',
16416      preorder_estimated_arrival: preorderData ? preorderData.estimated_arrival : null,
16417      preorder_note: preorderData ? preorderData.note : ''
16418    };
16419  }
16420
16421  /**
16422   * Main Buy Now function
16423   * Performs validation and redirects to checkout with buy_now mode
16424   */
16425  window.buyNow = async function(e) {
16426    if (e) e.preventDefault();
16427    
16428    try {
16429      // Check user login status
16430      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
16431                        document.body.classList.contains('logged-in') ||
16432                        window.userLoggedIn === true ||
16433                        document.body.dataset.userLoggedIn === 'true';
16434      
16435      if (!isLoggedIn) {
16436        // Redirect to login with return URL to current page
16437        const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
16438        window.location.href = '/login?return=' + currentUrl;
16439        return;
16440      }
16441
16442      // Get current product data
16443      const { productId, qty, variantId } = getCurrentProductData();
16444      
16445      // Create payload for buy now
16446      const payload = createBuyNowPayload(productId, qty, variantId);
16447      
16448      // Store buy now data in sessionStorage for checkout page
16449      sessionStorage.setItem('buyNowData', JSON.stringify(payload));
16450
16451      // Track add_to_cart (buy now counts as purchase intent)
16452      if (window.PlazaAnalytics) {
16453        window.PlazaAnalytics.track('add_to_cart', { slug: String(window._pit_slug || '') });
16454      }
16455
16456      // Redirect to checkout with buy_now mode
16457      window.location.href = '/checkout?mode=buy_now';
16458      
16459    } catch (err) {
16460      console.error('Buy Now error:', err);
16461      
16462      if (window.showToast) {
16463        window.showToast({ 
16464          type: 'warn', 
16465          title: 'Gagal memproses', 
16466          message: String(err?.message || 'Terjadi kesalahan saat memproses pembelian'), 
16467          duration: 4500 
16468        });
16469      } else {
16470        alert(err.message || 'Terjadi kesalahan saat memproses pembelian');
16471      }
16472    }
16473  };
16474
16475  /**
16476   * Alternative Buy Now with confirmation dialog
16477   */
16478  window.buyNowWithConfirm = async function(e) {
16479    if (e) e.preventDefault();
16480    
16481    try {
16482      const { productId, qty, variantId } = getCurrentProductData();
16483      
16484      const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
16485      const variantText = getVariantSummaryText();
16486      const priceTxt = document.getElementById('pd-price-current')?.textContent || 'Rp0';
16487      
16488      const confirmMessage = `Lanjut ke checkout?\n\nProduk: ${name}${variantText ? `\nVariant: ${variantText}` : ''}\nQty: ${qty}\nHarga: ${priceTxt}`;
16489      
16490      if (!confirm(confirmMessage)) {
16491        return;
16492      }
16493      
16494      // Proceed with buy now
16495      await window.buyNow(e);
16496      
16497    } catch (err) {
16498      console.error('Buy Now with confirm error:', err);
16499      alert(err.message || 'Terjadi kesalahan');
16500    }
16501  };
16502
16503  /**
16504   * Attach event listener for Buy with Voucher button
16505   */
16506  function setupBuyButtons() {
16507    // Setup desktop buy button (regular buy now without voucher)
16508    const buyBtn = document.getElementById('btn-buy');
16509    if (buyBtn) {
16510      buyBtn.addEventListener('click', function(e) {
16511        e.preventDefault();
16512        if (buyBtn.disabled) return;
16513        if (window.buyNow) {
16514          window.buyNow(e);
16515        }
16516      });
16517    }
16518    
16519    // Setup desktop buy with voucher button
16520    const voucherBtn = document.getElementById('btn-buy-voucher');
16521    if (voucherBtn) {
16522      voucherBtn.addEventListener('click', function(e) {
16523        e.preventDefault();
16524        
16525        // Validate stock availability before proceeding
16526        if (this.disabled || this.getAttribute('data-stock-blocked') === 'true') {
16527          const stockReason = this.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
16528          
16529          if (window.showToast) {
16530            window.showToast({
16531              type: 'warn',
16532              title: 'Stok Tidak Tersedia',
16533              message: stockReason,
16534              duration: 3500
16535            });
16536          } else {
16537            alert(stockReason);
16538          }
16539          return;
16540        }
16541        
16542        const voucherCode = this.dataset.voucherCode;
16543        if (voucherCode) {
16544          window.buyNowWithVoucher(e, voucherCode);
16545        }
16546      });
16547    }
16548    
16549  }
16550  
16551  // CRITICAL: Attach mobile button handler at DOCUMENT level with CAPTURE phase
16552  // This is the ONLY way to guarantee it fires regardless of other scripts
16553  document.addEventListener('click', function(e) {
16554    // Check if click is on mobile buy button or its children
16555    const target = e.target.closest('#mf-buy');
16556    if (!target) return; // Not our button
16557    
16558    e.preventDefault();
16559    e.stopPropagation();
16560    e.stopImmediatePropagation();
16561    
16562    if (target.disabled) return;
16563    
16564    // Get voucher code from parent FAB's data attribute
16565    const mobileFab = document.getElementById('pd-mobile-fab');
16566    const voucherCode = mobileFab ? (mobileFab.getAttribute('data-voucher-code') || '') : '';
16567    
16568    if (voucherCode && voucherCode.trim() !== '' && window.buyNowWithVoucher) {
16569      window.buyNowWithVoucher(e, voucherCode);
16570    } else if (window.buyNow) {
16571      window.buyNow(e);
16572    }
16573  }, true); // CAPTURE PHASE - fires BEFORE any bubble phase handlers
16574  
16575  // Execute immediately if DOM already loaded, otherwise wait
16576  if (document.readyState === 'loading') {
16577    document.addEventListener('DOMContentLoaded', setupBuyButtons);
16578  } else {
16579    setupBuyButtons();
16580  }
16581
16582  /**
16583   * Buy Now with Auto-Applied Voucher
16584   * Applies voucher to session before redirecting to checkout (like cart does)
16585   * 
16586   * @param {Event} e - Click event
16587   * @param {string} voucherCode - Voucher code to apply
16588   */
16589  window.buyNowWithVoucher = async function(e, voucherCode) {
16590    if (e) e.preventDefault();
16591    
16592    console.log('[buyNowWithVoucher] Called with voucher code:', voucherCode);
16593    
16594    try {
16595      // CRITICAL: Check stock availability first
16596      const voucherBtn = document.getElementById('btn-buy-voucher');
16597      if (voucherBtn) {
16598        if (voucherBtn.disabled || voucherBtn.getAttribute('data-stock-blocked') === 'true') {
16599          const stockReason = voucherBtn.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
16600          
16601          if (window.showToast) {
16602            window.showToast({
16603              type: 'warn',
16604              title: 'Stok Tidak Tersedia',
16605              message: stockReason,
16606              duration: 3500
16607            });
16608          } else {
16609            alert(stockReason);
16610          }
16611          return;
16612        }
16613      }
16614      
16615      // Check login status first
16616      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
16617                        document.body.classList.contains('logged-in') ||
16618                        window.userLoggedIn === true ||
16619                        document.body.dataset.userLoggedIn === 'true';
16620      
16621      console.log('[buyNowWithVoucher] User logged in:', isLoggedIn);
16622      
16623      if (!isLoggedIn) {
16624        const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
16625        window.location.href = '/login?return=' + currentUrl;
16626        return;
16627      }
16628
16629      // Get current product data
16630      const { productId, qty, variantId } = getCurrentProductData();
16631      const payload = createBuyNowPayload(productId, qty, variantId);
16632      
16633      console.log('[buyNowWithVoucher] Product data:', { productId, qty, variantId });
16634      
16635      // Get category path for voucher validation
16636      const categoryPath = window.PD_CATEGORY_PATH || '';
16637      
16638      // Step 1: Clear any existing voucher first (important for clean state)
16639      console.log('[buyNowWithVoucher] Step 1: Clearing existing voucher...');
16640      try {
16641        await fetch('/api/voucher/remove.php', {
16642          method: 'POST',
16643          headers: { 
16644            'X-CSRF-Token': getCsrfToken()
16645          },
16646          body: JSON.stringify({
16647            csrf_token: getCsrfToken()
16648          }),
16649          credentials: 'same-origin'
16650        });
16651      } catch (e) {
16652        // Ignore error if no voucher exists
16653        console.debug('No existing voucher to remove or removal failed');
16654      }
16655      
16656      // Step 2: Apply voucher to session (like cart does)
16657      console.log('[buyNowWithVoucher] Step 2: Applying voucher to session...');
16658      const applyRes = await fetch('/api/voucher/apply.php', {
16659        method: 'POST',
16660        headers: { 
16661          'Content-Type': 'application/json',
16662          'X-CSRF-Token': getCsrfToken()
16663        },
16664        credentials: 'same-origin',
16665        body: JSON.stringify({
16666          csrf_token: getCsrfToken(),
16667          code: voucherCode,
16668          items: [{
16669            pid: productId,
16670            price: payload.meta.price,
16671            qty: qty,
16672            category_path: categoryPath
16673          }],
16674          shipping_cost: null // Will be calculated in checkout
16675        })
16676      });
16677      
16678      const applyData = await applyRes.json();
16679      
16680      console.log('[buyNowWithVoucher] Voucher apply response:', applyData);
16681      
16682      if (!applyData.success) {
16683        // Voucher invalid - show error and fallback to regular buy now
16684        console.warn('[buyNowWithVoucher] Voucher application failed:', applyData.message);
16685        if (window.showToast) {
16686          window.showToast({
16687            type: 'error',
16688            title: 'Voucher Tidak Valid',
16689            message: applyData.message || 'Voucher tidak dapat diterapkan untuk produk ini',
16690            duration: 4000
16691          });
16692        } else {
16693          alert(applyData.message || 'Voucher tidak dapat diterapkan');
16694        }
16695        
16696        // Fallback to regular buy now without voucher
16697        console.log('Voucher failed, proceeding with regular buy now');
16698        return window.buyNow(e);
16699      }
16700      
16701      // Step 3: Store buy now data in sessionStorage
16702      console.log('[buyNowWithVoucher] Step 3: Storing buy now data in sessionStorage');
16703      sessionStorage.setItem('buyNowData', JSON.stringify(payload));
16704      
16705      // Step 4: Redirect to checkout
16706      // Voucher sudah di session, checkout akan auto-load via loadAppliedVoucher()
16707      console.log('[buyNowWithVoucher] Step 4: Redirecting to checkout with voucher applied');
16708      window.location.href = '/checkout?mode=buy_now';
16709      
16710    } catch (err) {
16711      console.error('Buy Now with Voucher error:', err);
16712      
16713      if (window.showToast) {
16714        window.showToast({
16715          type: 'error',
16716          title: 'Terjadi Kesalahan',
16717          message: 'Gagal memproses pembelian dengan voucher. Silakan coba lagi.',
16718          duration: 3000
16719        });
16720      } else {
16721        alert('Gagal memproses pembelian. Silakan coba lagi.');
16722      }
16723    }
16724  };
16725})();
16726</script>
vendor: 1 bytes, line 16726
16726
16727<script data-inlined="pd-voucher-recalc.js">
16728/**
16729 * Product Detail - Voucher Discount Recalculation
16730 * 
16731 * Handles dynamic voucher discount recalculation when variant price changes
16732 * Supports both PERCENTAGE and FIXED_AMOUNT voucher types
16733 */
16734
16735(function() {
16736  'use strict';
16737
16738  /**
16739   * Calculate voucher discount based on type and price
16740   * 
16741   * @param {string} type - Voucher type: 'PERCENTAGE' or 'FIXED_AMOUNT'
16742   * @param {number} value - Voucher value (percentage or fixed amount)
16743   * @param {number} price - Current variant price
16744   * @param {number} maxDiscount - Maximum discount for percentage vouchers (0 = no limit)
16745   * @return {number} Calculated discount amount
16746   */
16747  function calculateVoucherDiscount(type, value, price, maxDiscount) {
16748    if (type === 'PERCENTAGE') {
16749      // Calculate percentage discount
16750      let discount = (price * value) / 100;
16751      
16752      // Apply max discount cap if set
16753      if (maxDiscount > 0 && discount > maxDiscount) {
16754        discount = maxDiscount;
16755      }
16756      
16757      return Math.round(discount);
16758    } else {
16759      // FIXED_AMOUNT - discount doesn't change with price
16760      return Math.round(value);
16761    }
16762  }
16763
16764  /**
16765   * Format number as Rupiah without 'Rp' prefix
16766   * 
16767   * @param {number} num - Number to format
16768   * @return {string} Formatted number (e.g., "20.000")
16769   */
16770  function formatNumber(num) {
16771    return Math.round(num).toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
16772  }
16773
16774  /**
16775   * Update voucher button discount label
16776   * 
16777   * @param {number} newPrice - New variant price
16778   */
16779  function updateVoucherDiscount(newPrice) {
16780    const voucherBtn = document.getElementById('btn-buy-voucher');
16781    if (!voucherBtn) return;
16782
16783    // Get voucher data from button attributes
16784    const voucherType = voucherBtn.dataset.voucherType;
16785    const voucherValue = parseFloat(voucherBtn.dataset.voucherValue) || 0;
16786    const voucherMax = parseFloat(voucherBtn.dataset.voucherMax) || 0;
16787
16788    if (!voucherType || voucherValue === 0) return;
16789
16790    // Calculate new discount
16791    const newDiscount = calculateVoucherDiscount(voucherType, voucherValue, newPrice, voucherMax);
16792
16793    // Update the discount label
16794    const saveLabel = voucherBtn.querySelector('.bv-save');
16795    if (saveLabel) {
16796      saveLabel.textContent = '-Rp' + formatNumber(newDiscount);
16797    }
16798
16799    // Also update mobile FAB voucher display if exists
16800    updateMobileFabVoucher(newDiscount);
16801  }
16802
16803  /**
16804   * Update mobile FAB voucher value
16805   * 
16806   * @param {number} discountAmount - New discount amount
16807   */
16808  function updateMobileFabVoucher(discountAmount) {
16809    const fab = document.getElementById('pd-mobile-fab');
16810    if (fab) {
16811      fab.dataset.voucherValue = discountAmount.toString();
16812    }
16813  }
16814
16815  /**
16816   * Listen to variant change events and recalculate discount
16817   */
16818  function initVoucherRecalculation() {
16819    window.addEventListener('pd-variant-changed', function(e) {
16820      const detail = e.detail || {};
16821      const newPrice = detail.price || 0;
16822
16823      if (newPrice > 0) {
16824        updateVoucherDiscount(newPrice);
16825      }
16826    });
16827  }
16828
16829  // Initialize when DOM is ready
16830  if (document.readyState === 'loading') {
16831    document.addEventListener('DOMContentLoaded', initVoucherRecalculation);
16832  } else {
16833    initVoucherRecalculation();
16834  }
16835
16836})();
16837
16838</script>
vendor: 1 bytes, line 16838
16838
16839<script nonce="se3iyzEHMc9FQEB4wSyd0w==">document.addEventListener('DOMContentLoaded',function(){if(typeof ProductReviews!=='undefined'){ProductReviews.init(325);}const r=document.getElementById('pd-rating-inline');if(r){r.addEventListener('click',function(e){e.preventDefault();const s=document.getElementById('reviews');if(s){s.scrollIntoView({behavior:'smooth',block:'start'});s.classList.add('highlight-flash');setTimeout(()=>{s.classList.remove('highlight-flash')},1500)}});}});</script>
16839
16840
16841<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
16842(function(){function _0x9i0j(el){if(typeof gtag==='function'){gtag('event','qoala_insurance_click',{event_category:'Insurance',event_label:'Qoala Banner Click - Product Detail',link_url:'/plazait-protect',page_location:window.location.href,product_id:parseInt(el.getAttribute('data-product-id'))||0,product_name:el.getAttribute('data-product-name')||'',category_path:el.getAttribute('data-category-path')||'',custom_map:{custom_parameter_1:'qoala_banner_product_detail'}})}}
16843
16844function _0x1k2l(){var a=document.querySelector('.qoala-cta[data-ga-event="qoala_banner_click"]');if(a){a.addEventListener('click',function(e){_0x9i0j(this)})}}if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',_0x1k2l)}else{_0x1k2l()}})();
16845
16846(function(){const pwpSection=document.getElementById('pwp-deals-section');const pwpBadge=document.getElementById('pwp-trigger-badge');if(!pwpSection)return;const productId=parseInt(pwpSection.getAttribute('data-product-id'))||0;const variantId=parseInt(pwpSection.getAttribute('data-variant-id'))||0;const categoryPath=pwpSection.getAttribute('data-category-path')||'';if(!productId)return;function formatRupiah(n){return'Rp'+Math.floor(n).toLocaleString('id-ID')}function loadPWPDeals(){const url=`/api/pwp/get_by_product.php?product_id=${productId}${variantId?`&variant_id=${variantId}`:''}${categoryPath?`&category_path=${encodeURIComponent(categoryPath)}`:''}`;fetch(url).then(r=>r.json()).then(data=>{if(data.success&&data.promotions&&data.promotions.length>0){renderPWPDeals(data.promotions);pwpSection.hidden=false;if(pwpBadge)pwpBadge.hidden=false;setTimeout(function(){if(typeof window.adjustSidebarBanner==='function'){window.adjustSidebarBanner()}},150)}else{pwpSection.hidden=true;if(pwpBadge)pwpBadge.hidden=true}}).catch(err=>{console.error('PWP load error:',err);pwpSection.hidden=true;if(pwpBadge)pwpBadge.hidden=true})}function renderPWPDeals(promotions){const loading=pwpSection.querySelector('.pwp-deals-loading');const grid=document.getElementById('pwp-deals-grid');if(loading)loading.hidden=true;if(!grid)return;grid.innerHTML='';promotions.forEach(promo=>{promo.bundled_products.forEach(product=>{const card=document.createElement('div');card.className='pwp-deal-card';const discount=product.discount>0?product.discount:0;const discountPercent=product.regular_price>0?Math.round((discount/product.regular_price)*100):0;const imgUrl=product.image_url?`/uploads/products/${product.image_url}`:'/assets/img/no-image.png';card.innerHTML=`<div class="pwp-deal-thumb"><img src="${imgUrl}" alt="${escapeHtml(product.name)}" loading="lazy"></div><div class="pwp-deal-info"><h4 class="pwp-deal-name">${escapeHtml(product.name)}</h4><div class="pwp-deal-prices"><span class="pwp-deal-price">${formatRupiah(product.pwp_price)}</span>${product.regular_price>product.pwp_price?`<span class="pwp-deal-original">${formatRupiah(product.regular_price)}</span>`:''}</div></div>${discountPercent>0?`<span class="pwp-deal-badge">-${discountPercent}%</span>`:''}`;grid.appendChild(card)})})}function escapeHtml(str){const div=document.createElement('div');div.textContent=str||'';return div.innerHTML}loadPWPDeals();window.addEventListener('pd-variant-changed',function(e){const detail=e.detail||{};const newVid=detail.variant_id||0;const currentVid=parseInt(pwpSection.getAttribute('data-variant-id'))||0;if(newVid!==currentVid){pwpSection.setAttribute('data-variant-id',newVid);loadPWPDeals()}})})();
16847</script>
16847<footer class="site-footer footer-compact variant-stripes" role="contentinfo">
16848  <div class="ft-top ft-collapsible" id="ftTop">
16849    <div class="container ft-grid">
16850
16851      <div class="ft-col ft-help">
16852        <h4 class="ft-title">Butuh Bantuan?</h4>
16853        <p class="ft-desc">Hubungi kami melalui Official WhatsApp interaktif sekarang.</p>
16854        <a class="ft-wa-btn" href="https://mikale.me/whatsappIWANTOGEL" target="_blank" rel="nofollow noopener">
16855          <span class="ico" aria-hidden="true">
16856            <svg viewBox="0 0 24 24" width="18" height="18"><path fill="#25D366" d="M12.04 2c-5.52 0-10 4.43-10 9.9 0 1.75.47 3.45 1.38 4.94L2 22l5.3-1.38c1.43.78 3.05 1.19 4.7 1.19 5.52 0 10-4.43 10-9.9S17.56 2 12.04 2Zm5.86 13.9c-.25.7-1.23 1.28-1.7 1.33-.45.06-1.02.08-1.65-.1-.38-.12-.88-.29-1.52-.56-2.66-1.15-4.4-3.83-4.53-4.01-.13-.18-1.08-1.44-1.08-2.75 0-1.31.68-1.95.92-2.22.25-.27.54-.33.72-.33.18 0 .36.01.52.01.17 0 .39-.06.62.47.25.6.85 2.07.92 2.22.07.15.12.33.02.53-.1.2-.15.33-.3.5-.15.17-.32.39-.46.53-.15.15-.3.31-.13.6.18.3.8 1.31 1.73 2.12 1.19 1.03 2.17 1.36 2.48 1.5.31.14.49.12.67-.07.18-.18.77-.82.98-1.1.21-.27.42-.23.7-.14.27.09 1.72.81 2.02.95.3.14.5.22.58.35.08.13.08.72-.17 1.42Z"/></svg>
16857          </span>
16858          Kirim Pesan
16859        </a>
16860
16861        <p class="ft-desc sm-gap">Ikuti kami:</p>
16862        <div class="ft-social">
16863          <a aria-label="Facebook" href="https://facebook.com/PlazaIt/"><svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M13.5 22v-8.2h2.7l.4-3.2h-3.1V8c0-.9.3-1.5 1.6-1.5H17V3.7c-.3 0-1.2-.1-2.2-.1-2.2 0-3.8 1.3-3.8 3.9v2.2H8v3.2h3v8.1h2.5Z"/></svg></a>
16864          <a aria-label="Instagram" href="https://www.instagram.com/plazait"><svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M7.8 2h8.4A5.8 5.8 0 0 1 22 7.8v8.4A5.8 5.8 0 0 1 16.2 22H7.8A5.8 5.8 0 0 1 2 16.2V7.8A5.8 5.8 0 0 1 7.8 2Zm0 2A3.8 3.8 0 0 0 4 7.8v8.4A3.8 3.8 0 0 0 7.8 20h8.4a3.8 3.8 0 0 0 3.8-3.8
16864V7.8A3.8 3.8 0 0 0 16.2 4H7.8Zm9.9 1.7a1.1 1.1 0 1 1 0 2.3 1.1 1.1 0 0 1 0-2.3ZM12 7a5 5 0 1 1 0 10 5 5 0 0 1 0-10Zm0 2a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z"/></svg></a>
16865          <a aria-label="TikTok" href="https://www.tiktok.com/@plazait"><svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M18.9 7.4a6.4 6.4 0 0 1-3.2-1.2v6.3a5.47 5.47 0 1 1-5.5-5.5c.2 0 .4 0 .6.02v2.37a2.92 2.92 0 0 0-.6-.07 2.8 2.8 0 1 0 2.8 2.8V2h2.3a4.6 4.6 0 0 0 4.5 4.1v2.3Z"/></svg></a>
16866        </div>
16867      </div>
16868
16869            <!-- Collapsible Content Wrapper (Non-Homepage Only) -->
16870      <div class="ft-collapsible-content" id="ftCollapsible">
16871      
16872      <div class="ft-col">
16873        <h4 class="ft-title">Tentang IWANTOGEL</h4>
16874        <ul class="ft-links">
16875          <li><a href="https://loklokpro.com/">Tentang IWANTOGEL</a></li>
16876          <li><a href="https://loklokpro.com/">Kontak IWANTOGEL</a></li>
16877          <li><a href="https://loklokpro.com/">Lokasi Toko</a></li>
16878          <li><a href="https://loklokpro.com/">FAQ</a></li>
16879        </ul>
16880        <div class="ft-address">
16881          <p class="company">PT.IWANTOGEL</p>
16882                      <p>,Jl. IWANTOGEL. No.42, RT.1/RW.4, Kp. IWANTOGEL, Kecamatan IWANTOGEL, Kota IWANTOGEL,</p>
16883                      <p>Daerah Khusus Ibukota IWANTOGEL 888</p>
16884                  </div>
16885      </div>
16886
16887      <div class="ft-col">
16888        <h4 class="ft-title">Informasi</h4>
16889        <ul class="ft-links">
16890          <li><a href="/syarat-dan-ketentuan">Syarat &amp; Ketentuan</a></li>
16891          <li><a href="/kebijakan-privasi">Kebijakan Privasi</a></li>
16892          <li><a href="/kebijakan-cookie">Kebijakan Cookie</a></li>
16893          <li><a href="/pengiriman-dan-pengembalian">Pengiriman &amp; Pengembalian</a></li>
16894          <li><a href="/pembayaran-dan-cicilan">Pembayaran &amp; Cicilan</a></li>
16895          <li><a href="/garansi-dan-layanan">Garansi &amp; Layanan</a></li>
16896        </ul>
16897
16898        <div class="ft-payments payments-small" aria-label="Metode Pembayaran">
16899          <div class="pay-row">
16900            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-mandiri.png" alt="Mandiri" loading="lazy">
16901            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-bni.gif" alt="BNI" loading="lazy">
16902            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-bri.gif" alt="BRI" loading="lazy">
16903            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-qris.gif" alt="QRIS" loading="lazy">
16904            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-bca.gif" alt="BCA" loading="lazy">
16905          </div>
16906        </div>
16907      </div>
16908
16909            </div>
16910      <!-- End Collapsible Content -->
16911      
16912      <!-- Expand Button (Mobile Only) -->
16913      <button class="ft-expand-btn" id="ftExpandBtn" aria-label="Lihat semua informasi footer">
16914        <span class="expand-text">Selengkapnya</span>
16915        <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
16916          <path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/>
16917        </svg>
16918      </button>
16919      
16920    </div>
16921  </div>
16922  <div class="ft-bottom">
16923    <div class="container ft-bottom-inner">
16924      <div class="ft-copy">
16925        <span>ANGIN-RIBUT &copy; <span id="yearNow">2026</span></span>
16926        <span>PT ANGIN-RIBUT. All rights reserved.</span>
16927      </div>
16928    </div>
16929  </div>
16930  
16931          <style nonce="se3iyzEHMc9FQEB4wSyd0w==">
16932            /* Chat Widget Styles - Lightweight & Minimal */
16933            .chat-widget {
16934                position: fixed;
16935                bottom: 20px;
16936                right: 20px;
16937                z-index: 9999;
16938                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16939            }
16940            
16941            /* Hide floating button on mobile - use bottom nav instead */
16942            @media (max-width: 640px) {
16943                .chat-toggle-btn {
16944                    display: none !important;
16945                }
16946                
16947                /* Ensure popup is still visible on mobile even when button is hidden */
16948                .chat-widget {
16949                    display: block !important;
16950                }
16951                
16952                .chat-popup {
16953                    display: none; /* Hidden by default, shown via JS */
16954                }
16955                
16956                .chat-popup[style*="display: block"],
16957                .chat-popup[style*="display:block"] {
16958                    display: block !important;
16959                }
16960            }
16961            
16962            .chat-toggle-btn {
16963                width: 60px;
16964                height: 60px;
16965                border-radius: 50%;
16966                background: linear-gradient(135deg, #F28C1A 0%, #D97809 100%);
16967                border: none;
16968                color: white;
16969                cursor: pointer;
16970                box-shadow: 0 4px 12px rgba(242, 140, 26, 0.3);
16971                position: relative;
16972                transition: transform 0.2s, box-shadow 0.2s;
16973            }
16974            
16975            .chat-toggle-btn:hover {
16976                transform: scale(1.05);
16977                box-shadow: 0 6px 20px rgba(242, 140, 26, 0.4);
16978            }
16979            
16980            #chat-toggle-btn .chat-icon {
16981                position: absolute;
16982                top: 50%;
16983                left: 50%;
16984                transform: translate(-50%, -50%);
16985                width: 28px !important;
16986                height: 28px !important;
16987            }
16988            
16989            .chat-unread-badge {
16990                position: absolute;
16991                top: -5px;
16992                right: -5px;
16993                background: #ef4444;
16994                color: white;
16995                border-radius: 12px;
16996                padding: 2px 6px;
16997                font-size: 11px;
16998                font-weight: 600;
16999                min-width: 20px;
17000                text-align: center;
17001            }
17002            
17003            .chat-popup {
17004                position: absolute;
17005                bottom: 80px;
17006                right: 0;
17007                width: 380px;
17008                max-width: calc(100vw - 40px);
17009                height: 500px;
17010                max-height: calc(100vh - 120px);
17011                background: white;
17012                border-radius: 12px;
17013                box-shadow: 0 10px 40px rgba(0,0,0,0.2);
17014                display: flex;
17015                flex-direction: column;
17016                overflow: hidden;
17017            }
17018            
17019            .chat-popup-header {
17020                background: linear-gradient(135deg, #F28C1A 0%, #D97809 100%);
17021                color: white;
17022                padding: 16px 20px;
17023                display: flex;
17024                justify-content: space-between;
17025                align-items: center;
17026                flex-shrink: 0;
17027            }
17028            
17029            .chat-header-title {
17030                display: flex;
17031                flex-direction: column;
17032                gap: 2px;
17033                min-height: 0;
17034            }
17035            
17036            .chat-popup-header h3 {
17037                margin: 0;
17038                font-size: 18px;
17039                font-weight: 600;
17040                line-height: 1.2;
17041            }
17042            
17043            .chat-operating-hours {
17044                font-size: 11px;
17045                opacity: 0.9;
17046                font-weight: 400;
17047                display: flex;
17048                align-items: center;
17049                gap: 4px;
17050                line-height: 1.2;
17051            }
17052            
17053            .chat-operating-hours svg {
17054                width: 12px;
17055                height: 12px;
17056                opacity: 0.8;
17057                flex-shrink: 0;
17058            }
17059            
17060            .chat-header-actions {
17061                display: flex;
17062                align-items: center;
17063                gap: 8px;
17064            }
17065            
17066            /* Notification Toggle Button */
17067            .btn-notif-toggle {
17068                background: none;
17069                border: none;
17070                color: white;
17071                font-size: 18px;
17072                cursor: pointer;
17073                padding: 4px 8px;
17074                transition: all 0.2s;
17075                opacity: 0.8;
17076            }
17077            
17078            .btn-notif-toggle:hover {
17079                opacity: 1;
17080                transform: scale(1.1);
17081            }
17082            
17083            /* Icon states */
17084            .btn-notif-toggle[data-status="not-subscribed"] svg {
17085                opacity: 0.7;
17086            }
17087            
17088            .btn-notif-toggle[data-status="not-subscribed"]:hover svg {
17089                opacity: 1;
17090            }
17091            
17092            .btn-notif-toggle[data-status="subscribed"] svg {
17093                opacity: 1;
17094                animation: bellRing 2s ease-in-out;
17095            }
17096            
17097            .btn-notif-toggle[data-status="blocked"] svg {
17098                color: #ff6b6b;
17099            }
17100            
17101            .btn-notif-toggle[data-status="not-supported"] {
17102                display: none !important;
17103            }
17104            
17105            @keyframes bellRing {
17106                0%, 100% { transform: rotate(0deg); }
17107                10%, 30% { transform: rotate(-10deg); }
17108                20%, 40% { transform: rotate(10deg); }
17109            }
17110            
17111            /* Notification Info Bar */
17112            .chat-notif-info-bar {
17113                position: absolute;
17114                top: 64px;
17115                left: 0;
17116                right: 0;
17117                background: linear-gradient(to right, #e3f2fd, #f5f5f5);
17118                border-bottom: 1px solid #bbdefb;
17119                padding: 8px 12px;
17120                display: flex;
17121                align-items: center;
17122                gap: 8px;
17123                font-size: 12px;
17124                color: #616161;
17125                z-index: 10;
17126                animation: slideDown 0.3s ease;
17127            }
17128            
17129            .chat-notif-info-bar svg {
17130                flex-shrink: 0;
17131            }
17132            
17133            .chat-notif-info-bar .small {
17134                flex: 1;
17135                line-height: 1.4;
17136            }
17137            
17138            .chat-notif-info-bar .btn-dismiss {
17139                background: none;
17140                border: none;
17141                color: #9e9e9e;
17142                cursor: pointer;
17143                padding: 0 4px;
17144                font-size: 16px;
17145                transition: color 0.2s;
17146            }
17147            
17148            .chat-notif-info-bar .btn-dismiss:hover {
17149                color: #616161;
17150            }
17151            
17152            @keyframes slideDown {
17153                from {
17154                    max-height: 0;
17155                    opacity: 0;
17156                }
17157                to {
17158                    max-height: 50px;
17159                    opacity: 1;
17160                }
17161            }
17162            
17163            .chat-close-btn {
17164                background: none;
17165                border: none;
17166                color: white;
17167                font-size: 28px;
17168                line-height: 1;
17169                cursor: pointer;
17170                padding: 0;
17171                width: 30px;
17172                height: 30px;
17173            }
17174            
17175            .chat-popup-body {
17176                flex: 1 1 auto;
17177                display: flex;
17178                flex-direction: column;
17179                min-height: 0;
17180                overflow: hidden;
17181                height: calc(100% - 64px);
17182                position: relative;
17183                transition: padding-top 0.3s ease;
17184            }
17185            
17186            /* Add padding when info bar is visible */
17187            .chat-popup-body.has-info-bar {
17188                padding-top: 40px;
17189            }
17190            
17191            .chat-view {
17192                flex: 1 1 auto;
17193                display: none;
17194                flex-direction: column;
17195                min-height: 0;
17196                overflow: hidden;
17197            }
17198            
17199            #chat-list-view {
17200                display: flex;
17201            }
17202            
17203            #chat-messages-view {
17204                display: none;
17205            }
17206            
17207            #chat-messages-view.active {
17208                display: flex;
17209                flex-direction: column;
17210            }
17211            
17212            #chat-new-view {
17213                display: none;
17214            }
17215            
17216            #chat-new-view.active {
17217                display: flex;
17218            }
17219            
17220            .chat-conversations-list {
17221                flex: 1;
17222                overflow-y: auto;
17223                padding: 0;
17224                background: white;
17225            }
17226            
17227            /* Custom Scrollbar for Conversations List */
17228            .chat-conversations-list::-webkit-scrollbar {
17229                width: 6px;
17230            }
17231            
17232            .chat-conversations-list::-webkit-scrollbar-track {
17233                background: #f1f1f1;
17234            }
17235            
17236            .chat-conversations-list::-webkit-scrollbar-thumb {
17237                background: #F28C1A;
17238                border-radius: 3px;
17239            }
17240            
17241            .chat-conversations-list::-webkit-scrollbar-thumb:hover {
17242                background: #D97809;
17243            }
17244            
17245            /* Firefox */
17246            .chat-conversations-list {
17247                scrollbar-width: thin;
17248                scrollbar-color: #F28C1A #f1f1f1;
17249            }
17250            
17251            .chat-conversation-item {
17252                padding: 12px 16px;
17253                cursor: pointer;
17254                display: flex;
17255                gap: 12px;
17256                align-items: flex-start;
17257                transition: background 0.15s ease;
17258                color: #33371f;
17259                border-bottom: 1px solid #f0f0f0;
17260            }
17261            
17262            .chat-conversation-item:hover {
17263                background: #f5f5f5;
17264            }
17265            
17266            .chat-conversation-item.active {
17267                background: #FFF5EB;
17268            }
17269            
17270            /* WhatsApp-like Avatar */
17271            .conversation-avatar {
17272                width: 48px;
17273                height: 48px;
17274                border-radius: 50%;
17275                background: linear-gradient(135deg, #F28C1A 0%, #D97809 100%);
17276                flex-shrink: 0;
17277                display: flex;
17278                align-items: center;
17279                justify-content: center;
17280                color: white;
17281                font-weight: 600;
17282                font-size: 18px;
17283            }
17284            
17285            .conversation-avatar svg {
17286                width: 24px;
17287                height: 24px;
17288            }
17289            
17290            /* WhatsApp-like Content */
17291            .conversation-content {
17292                flex: 1;
17293                min-width: 0;
17294                display: flex;
17295                flex-direction: column;
17296                gap: 4px;
17297            }
17298            
17299            .conversation-header {
17300                display: flex;
17301                justify-content: space-between;
17302                align-items: center;
17303                gap: 8px;
17304            }
17305            
17306            .conversation-name {
17307                font-weight: 600;
17308                font-size: 15px;
17309                color: #111;
17310                white-space: nowrap;
17311                overflow: hidden;
17312                text-overflow: ellipsis;
17313            }
17314            
17315            .conversation-time {
17316                font-size: 12px;
17317                color: #7d8166;
17318                flex-shrink: 0;
17319            }
17320            
17321            .conversation-preview {
17322                display: flex;
17323                justify-content: space-between;
17324                align-items: center;
17325                gap: 8px;
17326            }
17327            
17328            .conversation-message {
17329                font-size: 14px;
17330                color: #667781;
17331                white-space: nowrap;
17332                overflow: hidden;
17333                text-overflow: ellipsis;
17334                margin: 0;
17335                flex: 1;
17336            }
17337            
17338            .conversation-unread {
17339                background: #25D366;
17340                color: white;
17341                border-radius: 12px;
17342                padding: 2px 8px;
17343                font-size: 12px;
17344                font-weight: 600;
17345                min-width: 20px;
17346                text-align: center;
17347                flex-shrink: 0;
17348            }
17349            
17350            /* Active item styling */
17351            .chat-conversation-item.active .conversation-name {
17352                color: #F28C1A;
17353            }
17354            
17355            .chat-conversation-item.active .conversation-time {
17356                color: #F28C1A;
17357            }
17358            
17359            .chat-new-btn {
17360                margin: 12px;
17361                padding: 12px;
17362                background: linear-gradient(135deg, #F28C1A 0%, #D97809 100%);
17363                color: white;
17364                border: none;
17365                border-radius: 8px;
17366                cursor: pointer;
17367                font-weight: 500;
17368                font-size: 14px;
17369                display: flex;
17370                align-items: center;
17371                justify-content: center;
17372                gap: 8px;
17373                transition: all 0.2s;
17374                flex-shrink: 0;
17375            }
17376            
17377            .chat-new-btn:hover {
17378                transform: translateY(-1px);
17379                box-shadow: 0 4px 8px rgba(242, 140, 26, 0.3);
17380            }
17381            
17382            .chat-conversation-title {
17383                flex: 1;
17384                font-weight: 600;
17385                font-size: 16px;
17386                color: #1f2937;
17387            }
17388            
17389            .chat-messages-container {
17390                flex: 1;
17391                overflow-y: auto;
17392                padding: 16px;
17393                display: flex;
17394                flex-direction: column;
17395                gap: 8px;
17396                min-height: 0;
17397                background: #E5DDD5;
17398            }
17399            
17400            /* Custom Scrollbar for Messages Container */
17401            .chat-messages-container::-webkit-scrollbar {
17402                width: 6px;
17403            }
17404            
17405            .chat-messages-container::-webkit-scrollbar-track {
17406                background: rgba(0, 0, 0, 0.05);
17407            }
17408            
17409            .chat-messages-container::-webkit-scrollbar-thumb {
17410                background: rgba(242, 140, 26, 0.6);
17411                border-radius: 3px;
17412            }
17413            
17414            .chat-messages-container::-webkit-scrollbar-thumb:hover {
17415                background: #F28C1A;
17416            }
17417            
17418            /* Firefox */
17419            .chat-messages-container {
17420                scrollbar-width: thin;
17421                scrollbar-color: rgba(242, 140, 26, 0.6) rgba(0, 0, 0, 0.05);
17422            }
17423            
17424            /* WhatsApp Group-like Messages */
17425            .chat-message {
17426                display: flex;
17427                margin-bottom: 4px;
17428                animation: fadeIn 0.2s ease;
17429            }
17430            
17431            @keyframes fadeIn {
17432                from { opacity: 0; transform: translateY(10px); }
17433                to { opacity: 1; transform: translateY(0); }
17434            }
17435            
17436            .chat-message.mine {
17437                justify-content: flex-end;
17438            }
17439            
17440            .chat-message-bubble {
17441                max-width: 80%;
17442                padding: 6px 8px 8px 10px;
17443                border-radius: 8px;
17444                position: relative;
17445                box-shadow: 0 1px 0.5px rgba(0,0,0,0.13);
17446            }
17447            
17448            /* Bubble from others (Admin/CS) */
17449            .chat-message:not(.mine) .chat-message-bubble {
17450                background: #FFFFFF;
17451                border-radius: 0 8px 8px 8px;
17452            }
17453            
17454            /* Bubble tail for others */
17455            .chat-message:not(.mine) .chat-message-bubble::before {
17456                content: '';
17457                position: absolute;
17458                top: 0;
17459                left: -8px;
17460                width: 0;
17461                height: 0;
17462                border-style: solid;
17463                border-width: 0 8px 13px 0;
17464                border-color: transparent #FFFFFF transparent transparent;
17465            }
17466            
17467            /* Bubble from user (mine) */
17468            .chat-message.mine .chat-message-bubble {
17469                background: #e4f5d0; /* Light blue for user messages */
17470                border-radius: 8px 0 8px 8px;
17471            }
17472            
17473            /* Bubble tail for mine */
17474            .chat-message.mine .chat-message-bubble::before {
17475                content: '';
17476                position: absolute;
17477                top: 0;
17478                right: -8px;
17479                width: 0;
17480                height: 0;
17481                border-style: solid;
17482                border-width: 0 0 13px 8px;
17483                border-color: transparent transparent transparent #eaf5d0;
17484            }
17485            
17486            .message-sender {
17487                font-size: 12.5px;
17488                font-weight: 600;
17489                color: #F28C1A !important;
17490                margin-bottom: 4px;
17491            }
17492            
17493            .chat-message.mine .message-sender {
17494                display: none;
17495            }
17496            
17497            /* Message text */
17498            .message-text {
17499                font-size: 14px;
17500                line-height: 19px;
17501                color: #ffffff !important;
17502                word-wrap: break-word;
17503                white-space: pre-wrap;
17504                margin-bottom: 4px;
17505            }
17506            
17507            /* Message metadata (time + status) */
17508            .message-meta {
17509                display: flex;
17510                align-items: center;
17511                justify-content: flex-end;
17512                gap: 4px;
17513                margin-top: 2px;
17514            }
17515            
17516            .message-time {
17517                font-size: 11px;
17518                color: #728166 !important;
17519                line-height: 15px;
17520            }
17521            
17522            /* Use multiple selectors to override global styles */
17523            .chat-message-bubble .message-status,
17524            .message-meta .message-status,
17525            .chat-message .message-status {
17526                width: 16px !important;
17527                height: 16px !important;
17528                min-width: 16px !important;
17529                min-height: 16px !important;
17530                max-width: 16px !important;
17531                max-height: 16px !important;
17532                flex-shrink: 0;
17533                color: #728166 !important;
17534                fill: currentColor !important;
17535            }
17536            
17537            .chat-message:not(.mine) .message-status {
17538                display: none;
17539            }
17540            
17541            /* Loading dots animation */
17542            @keyframes loadingDots {
17543                0%, 20% { content: '.'; }
17544                40% { content: '..'; }
17545                60%, 100% { content: '...'; }
17546            }
17547            
17548            .message-status.loading::after {
17549                content: '...';
17550                animation: loadingDots 1.5s infinite;
17551                font-size: 16px;
17552                line-height: 16px;
17553            }
17554            
17555            .message-status.loading path {
17556                display: none;
17557            }
17558            
17559            .message-status.failed {
17560                color: #ef4444 !important;
17561                cursor: pointer;
17562            }
17563            
17564            .message-status.sent {
17565                color: #667781 !important;
17566            }
17567            
17568            .chat-new-form {
17569                flex: 1;
17570                overflow-y: auto;
17571                padding: 16px;
17572                display: flex;
17573                flex-direction: column;
17574            }
17575            
17576            .chat-new-form form {
17577                display: flex;
17578                flex-direction: column;
17579                gap: 16px;
17580                flex: 1;
17581            }
17582            
17583            .chat-new-form .form-group {
17584                display: flex;
17585                flex-direction: column;
17586                gap: 6px;
17587            }
17588            
17589            .chat-new-form label {
17590                font-size: 14px;
17591                font-weight: 500;
17592                color: #374151;
17593            }
17594            
17595            .chat-new-form .form-control {
17596                width: 100%;
17597                padding: 10px 12px;
17598                border: 1px solid #d1d5db;
17599                border-radius: 8px;
17600                font-size: 14px;
17601                font-family: inherit;
17602                transition: border-color 0.2s;
17603                box-sizing: border-box;
17604            }
17605            
17606            .chat-new-form .form-control:focus {
17607                outline: none;
17608                border-color: #F28C1A;
17609                box-shadow: 0 0 0 3px rgba(242, 140, 26, 0.1);
17610            }
17611            
17612            .chat-new-form textarea.form-control {
17613                resize: vertical;
17614                min-height: 100px;
17615            }
17616            
17617            .chat-new-form .btn {
17618                padding: 12px 20px;
17619                border: none;
17620                border-radius: 8px;
17621                font-size: 15px;
17622                font-weight: 500;
17623                cursor: pointer;
17624                transition: all 0.2s;
17625                text-align: center;
17626            }
17627            
17628            .chat-new-form .btn-primary {
17629                background: linear-gradient(135deg, #F28C1A 0%, #D97809 100%);
17630                color: white;
17631            }
17632            
17633            .chat-new-form .btn-primary:hover {
17634                transform: translateY(-1px);
17635                box-shadow: 0 4px 8px rgba(242, 140, 26, 0.3);
17636            }
17637            
17638            .chat-new-form .btn-primary:active {
17639                transform: scale(0.98);
17640            }
17641            
17642            .chat-new-form .btn-block {
17643                width: 100%;
17644                margin-top: auto;
17645            }
17646            
17647            .chat-message-form {
17648                padding: 12px;
17649                border-top: 1px solid #e5e7eb;
17650                display: flex;
17651                flex-wrap: wrap;
17652                gap: 8px;
17653                align-items: flex-end;
17654                flex-shrink: 0;
17655                background: white;
17656            }
17657            
17658            .chat-product-card {
17659                flex: 1 1 100%;
17660                max-width: 100%;
17661                display: flex;
17662                gap: 12px;
17663                padding: 12px;
17664                background: #f3f4f6;
17665                border-radius: 8px;
17666                margin-bottom: 4px;
17667                border: 1px solid #e5e7eb;
17668            }
17669            
17670            .chat-product-card img {
17671                width: 60px;
17672                height: 60px;
17673                object-fit: cover;
17674                border-radius: 6px;
17675                flex-shrink: 0;
17676            }
17677            
17678            .chat-product-card-content {
17679                flex: 1;
17680                min-width: 0;
17681            }
17682            
17683            .chat-product-card-name {
17684                font-weight: 500;
17685                font-size: 14px;
17686                color: #1f2937;
17687                margin-bottom: 4px;
17688                white-space: nowrap;
17689                overflow: hidden;
17690                text-overflow: ellipsis;
17691            }
17692            
17693            .chat-product-card-variant {
17694                font-size: 12px;
17695                color: #74806b;
17696                margin-bottom: 4px;
17697            }
17698            
17699            .chat-product-card-price {
17700                font-size: 13px;
17701                color: #8bea66;
17702                font-weight: 600;
17703            }
17704            
17705            .chat-product-card-remove {
17706                align-self: flex-start;
17707                background: none;
17708                border: none;
17709                color: #9ca3af;
17710                cursor: pointer;
17711                padding: 4px;
17712                font-size: 18px;
17713                line-height: 1;
17714                transition: color 0.2s;
17715            }
17716            
17717            .chat-product-card-remove:hover {
17718                color: #ef4444;
17719            }
17720            
17721            /* Product card in messages (read-only display) */
17722            .message-product-card {
17723                display: flex;
17724                gap: 10px;
17725                padding: 10px;
17726                background: rgba(255, 255, 255, 0.5);
17727                border-radius: 8px;
17728                margin-bottom: 6px;
17729                border: 1px solid rgba(0, 0, 0, 0.08);
17730                align-items: center;
17731            }
17732            
17733            .chat-message.mine .message-product-card {
17734                background: rgba(255, 255, 255, 0.7);
17735            }
17736            
17737            .message-product-card img {
17738                width: 50px;
17739                height: 50px;
17740                object-fit: cover;
17741                border-radius: 6px;
17742                flex-shrink: 0;
17743            }
17744            
17745            .message-product-info {
17746                flex: 1;
17747                min-width: 0;
17748            }
17749            
17750            .message-product-name {
17751                font-weight: 500;
17752                font-size: 13px;
17753                color: #33371f;
17754                margin-bottom: 2px;
17755                overflow: hidden;
17756                text-overflow: ellipsis;
17757                display: -webkit-box;
17758                -webkit-line-clamp: 2;
17759                -webkit-box-orient: vertical;
17760                line-height: 1.3;
17761            }
17762            
17763            .message-product-variant {
17764                font-size: 11px;
17765                color: #78806b;
17766                margin-bottom: 2px;
17767            }
17768            
17769            .message-product-price {
17770                font-size: 12px;
17771                color: #c7ea66;
17772                font-weight: 600;
17773            }
17774            
17775            .message-product-link {
17776                flex-shrink: 0;
17777                width: 32px;
17778                height: 32px;
17779                display: flex;
17780                align-items: center;
17781                justify-content: center;
17782                background: rgba(102, 126, 234, 0.1);
17783                border-radius: 6px;
17784                color: #c7ea66;
17785                text-decoration: none;
17786                transition: all 0.2s;
17787            }
17788            
17789            .message-product-link:hover {
17790                background: #b7ea66;
17791                color: white;
17792            }
17793            
17794            .message-product-link svg {
17795                width: 16px;
17796                height: 16px;
17797            }
17798            
17799            .chat-message-input {
17800                flex: 1;
17801                border: 1px solid #d1d5db;
17802                border-radius: 20px;
17803                padding: 10px 16px;
17804                resize: none;
17805                max-height: 100px;
17806                font-family: inherit;
17807                font-size: 14px;
17808                line-height: 1.5;
17809                box-sizing: border-box;
17810                overflow-y: hidden;
17811            }
17812            
17813            .chat-message-input:focus {
17814                outline: none;
17815                border-color: #F28C1A;
17816                box-shadow: 0 0 0 3px rgba(242, 140, 26, 0.1);
17817            }
17818            
17819            .chat-send-btn {
17820                width: 40px;
17821                height: 40px;
17822                border-radius: 50%;
17823                background: linear-gradient(135deg, #F28C1A 0%, #D97809 100%);
17824                border: none;
17825                color: white;
17826                cursor: pointer;
17827                flex-shrink: 0;
17828                display: flex;
17829                align-items: center;
17830                justify-content: center;
17831                transition: all 0.2s;
17832            }
17833            
17834            .chat-send-btn:hover {
17835                transform: scale(1.05);
17836                box-shadow: 0 2px 8px rgba(242, 140, 26, 0.3);
17837            }
17838            
17839            .chat-send-btn:active {
17840                transform: scale(0.95);
17841            }
17842            
17843            .chat-loading {
17844                text-align: center;
17845                padding: 20px;
17846                color: #6b7280;
17847            }
17848            
17849            /* Date Separator (WhatsApp-style) */
17850            .date-separator {
17851                display: flex;
17852                align-items: center;
17853                gap: 12px;
17854                margin: 24px 0 16px;
17855                opacity: 0;
17856                animation: fadeInSeparator 0.3s ease forwards;
17857            }
17858
17859            @keyframes fadeInSeparator {
17860                from {
17861                    opacity: 0;
17862                    transform: translateY(-10px);
17863                }
17864                to {
17865                    opacity: 1;
17866                    transform: translateY(0);
17867                }
17868            }
17869
17870            .date-separator-line {
17871                flex: 1;
17872                height: 1px;
17873                background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.1) 50%, transparent);
17874            }
17875
17876            .date-separator-text {
17877                flex-shrink: 0;
17878                padding: 4px 12px;
17879                background: #e9ecef;
17880                color: #6c757d;
17881                font-size: 11px;
17882                font-weight: 600;
17883                text-transform: uppercase;
17884                letter-spacing: 0.5px;
17885                border-radius: 12px;
17886                box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
17887            }
17888            
17889            /* Mobile responsive */
17890            @media (max-width: 640px) {
17891                .chat-popup {
17892                    position: fixed !important; /* Override absolute positioning */
17893                    bottom: 45px !important; /* Above bottom nav */
17894                    right: 0 !important;
17895                    left: 0 !important;
17896                    width: 100% !important;
17897                    max-width: 100% !important;
17898                    /* Use dvh (dynamic viewport height) for browser UI awareness */
17899                    height: calc(100dvh - 45px) !important;
17900                    max-height: calc(100dvh - 45px) !important;
17901                    /* Fallback for browsers that don't support dvh */
17902                    height: calc(var(--viewport-height, 100vh) - 45px) !important;
17903                    max-height: calc(var(--viewport-height, 100vh) - 45px) !important;
17904                    border-radius: 0 !important;
17905                    z-index: 9999 !important;
17906                }
17907                
17908                .chat-popup-header {
17909                    border-radius: 0;
17910                }
17911                
17912                .chat-messages-container,
17913                .chat-conversations-list {
17914                    /* Adjust for mobile keyboard using dynamic viewport */
17915                    max-height: calc(100dvh - 190px);
17916                    /* Fallback for browsers that don't support dvh */
17917                    max-height: calc(var(--viewport-height, 100vh) - 190px);
17918                }
17919            }
17920        </style>
17921                <!-- Chat Widget Button -->
17922        <div id="chat-widget" class="chat-widget" 
17923             data-user-id=""
17924             data-is-logged-in="false"
17925             data-csrf-token="df5d776748b1ed8c8dd0d987bae99b1ee3dea6c6dcbe0eedb1bea766c56d569a">
17926            <button id="chat-toggle-btn" class="chat-toggle-btn" aria-label="Toggle chat">
17927                <svg class="chat-icon" width="28" height="28" viewBox="0 0 640 640" fill="currentColor">
17928                    <path d="M320 128C241 128 175.3 185.3 162.3 260.7C171.6 257.7 181.6 256 192 256L208 256C234.5 256 256 277.5 256 304L256 400C256 426.5 234.5 448 208 448L192 448C139 448 96 405 96 352L96 288C96 164.3 196.3 64 320 64C443.7 64 544 164.3 544 288L544 456.1C544 522.4 490.2 576.1 423.9 576.1L336 576L304 576C277.5 576 256 554.5 256 528C256 501.5 277.5 480 304 480L336 480C362.5 480 384 501.5 384 528L384 528L424 528C463.8 528 496 495.8 496 456L496 435.1C481.9 443.3 465.5 447.9 448 447.9L432 447.9C405.5 447.9 384 426.4 384 399.9L384 303.9C384 277.4 405.5 255.9 432 255.9L448 255.9C458.4 255.9 468.3 257.5 477.7 260.6C464.7 185.3 399.1 127.9 320 127.9z"/>
17929                </svg>
17930                <span id="chat-unread-badge" class="chat-unread-badge" style="display: none;">0</span>
17931            </button>
17932            
17933            <div id="chat-popup" class="chat-popup" style="display: none;">
17934                <div class="chat-popup-header">
17935                    <div class="chat-header-title">
17936                        <h3>Bantuan</h3>
17937                        <span id="chatOperatingHours" class="chat-operating-hours" style="display: none;"></span>
17938                    </div>
17939                    <div class="chat-header-actions">
17940                        <button id="chatNotifToggle" class="btn-notif-toggle" data-status="not-supported" title="Push notification" style="display: none;">
17941                            <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
17942                                <path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/>
17943                            </svg>
17944                        </button>
17945                        <button id="chat-close-btn" class="chat-close-btn" aria-label="Close chat">×</button>
17946                    </div>
17947                </div>
17948                
17949                <!-- Notification Info Bar (conditional) -->
17950                <div class="chat-notif-info-bar" id="chatNotifInfoBar" style="display:none;">
17951                    <svg width="14" height="14" viewBox="0 0 24 24" fill="#ffc107">
17952                        <path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"/>
17953                    </svg>
17954                    <span class="small">Aktifkan notifikasi untuk pemberitahuan balasan</span>
17955                    <button class="btn-dismiss" onclick="ChatNotification.dismissInfo()">✕</button>
17956                </div>
17957                
17958                <div class="chat-popup-body">
17959                    <!-- Conversations List View -->
17960                    <div id="chat-list-view" class="chat-view">
17961                        <div id="chat-conversations-list" class="chat-conversations-list">
17962                            <div class="chat-loading">Memuat...</div>
17963                            <!-- Conversation Item Structure (rendered by JS):
17964                            <div class="chat-conversation-item">
17965                                <div class="conversation-avatar">
17966                                    <svg>...</svg>
17967                                </div>
17968                                <div class="conversation-content">
17969                                    <div class="conversation-header">
17970                                        <span class="conversation-name">Admin Support</span>
17971                                        <span class="conversation-time">10:30</span>
17972                                    </div>
17973                                    <div class="conversation-preview">
17974                                        <p class="conversation-message">Terima kasih telah menghubungi kami...</p>
17975                                        <span class="conversation-unread">2</span>
17976                                    </div>
17977                                </div>
17978                            </div>
17979                            -->
17980                        </div>
17981                        <button id="chat-new-btn" class="chat-new-btn">
17982                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
17983                                <line x1="12" y1="5" x2="12" y2="19"></line>
17984                                <line x1="5" y1="12" x2="19" y2="12"></line>
17985                            </svg>
17986                            Percakapan Baru
17987                        </button>
17988                    </div>
17989                    
17990                    <!-- Chat Messages View -->
17991                    <div id="chat-messages-view" class="chat-view" style="display: none;">
17992                        <div id="chat-messages-container" class="chat-messages-container">
17993                            <div class="chat-loading">Memuat pesan...</div>
17994                            <!-- Message Structure (rendered by JS):
17995                            <div class="chat-message"> (or .chat-message.mine)
17996                                <div class="chat-message-bubble">
17997                                    <div class="message-sender">Admin Support</div> (only for non-mine)
17998                                    <div class="message-text">Halo, ada yang bisa dibantu?</div>
17999                                    <div class="message-meta">
18000                                        <span class="message-time">10:30</span>
18001                                        <svg class="message-status">...</svg> (only for mine)
18002                                    </div>
18003                                </div>
18004                            </div>
18005                            -->
18006                        </div>
18007                        
18008                        <form id="chat-message-form" class="chat-message-form">
18009                            <input type="hidden" id="chat-current-conversation-id" value="">
18010                            <textarea 
18011                                id="chat-message-input" 
18012                                class="chat-message-input" 
18013                                placeholder="Ketik pesan..."
18014                                rows="1"
18015                                maxlength="5000"
18016                            ></textarea>
18017                            <button type="submit" class="chat-send-btn" aria-label="Send message">
18018                                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
18019                                    <line x1="22" y1="2" x2="11" y2="13"></line>
18020                                    <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
18021                                </svg>
18022                            </button>
18023                        </form>
18024                    </div>
18025                    
18026                    <!-- New Chat View -->
18027                    <div id="chat-new-view" class="chat-view" style="display: none;">
18028                        <div class="chat-messages-header">
18029                            <button id="chat-new-back-btn" class="chat-back-btn" aria-label="Back to list">
18030                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
18031                                    <path d="M19 12H5M12 19l-7-7 7-7"/>
18032                                </svg>
18033                            </button>
18034                            <div class="chat-conversation-title">Percakapan Baru</div>
18035                        </div>
18036                        
18037                        <div class="chat-new-form">
18038                            <form id="chat-start-form">
18039                                <div class="form-group">
18040                                    <label for="chat-new-subject">Subjek</label>
18041                                    <input 
18042                                        type="text" 
18043                                        id="chat-new-subject" 
18044                                        class="form-control" 
18045                                        placeholder="Bantuan Pelanggan"
18046                                        maxlength="255"
18047                                    >
18048                                </div>
18049                                <div class="form-group">
18050                                    <label for="chat-new-message">Pesan</label>
18051                                    <textarea 
18052                                        id="chat-new-message" 
18053                                        class="form-control" 
18054                                        rows="4" 
18055                                        placeholder="Tulis pertanyaan Anda..."
18056                                        required
18057                                        maxlength="5000"
18058                                    ></textarea>
18059                                </div>
18060                                <button type="submit" class="btn btn-primary btn-block">Kirim</button>
18061                            </form>
18062                        </div>
18063                    </div>
18064                </div>
18065            </div>
18066        </div>
18067        
18068        <!-- Login Required Modal -->
18069        <div class="modal-overlay" id="chatLoginModal">
18070            <div class="modal-dialog">
18071                <div class="modal-header">
18072                    <h3 class="modal-title">
18073                        <div class="modal-icon info">
18074                            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18075                                <path d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z"/>
18076                            </svg>
18077                        </div>
18078                        Login Diperlukan
18079                    </h3>
18080                </div>
18081                <div class="modal-body">
18082                    <p class="modal-message">
18082Anda harus login terlebih dahulu untuk menggunakan fitur chat bantuan pelanggan.</p>
18083                    <div class="modal-actions">
18084                        <button class="modal-btn modal-btn-secondary" id="chatLoginCancel">Batal</button>
18085                        <button class="modal-btn modal-btn-primary" id="chatLoginConfirm">Login Sekarang</button>
18086                    </div>
18087                </div>
18088            </div>
18089        </div>
18090        
18090<script src="https://plazait.co.id/assets/js/chat-widget.js" nonce="se3iyzEHMc9FQEB4wSyd0w=="></script>
18090<script data-inlined="chat-notification.js">
18091/**
18092 * Customer Chat Push Notification Manager
18093 * Handles browser push notification subscription for customers
18094 * Version: 1.0.0
18095 * Date: 2026-01-19
18096 */
18097
18098const ChatNotification = {
18099    // State
18100    status: 'not-supported', // not-supported, not-subscribed, subscribed, blocked
18101    subscription: null,
18102    vapidPublicKey: null, // Will be loaded from server
18103    
18104    /**
18105     * Initialize notification manager
18106     */
18107    async init() {
18108        // Check if user is logged in (only for logged-in users)
18109        if (!this.isUserLoggedIn()) {
18110            this.updateUI('not-supported');
18111            return;
18112        }
18113        
18114        // Check browser support
18115        if (!this.isSupported()) {
18116            this.updateUI('not-supported');
18117            return;
18118        }
18119        
18120        // Load VAPID key from server
18121        await this.loadVapidKey();
18122        
18123        if (!this.vapidPublicKey) {
18124            this.updateUI('not-supported');
18125            return;
18126        }
18127        
18128        // Register service worker if needed
18129        await this.registerServiceWorker();
18130        
18131        // Check current permission
18132        const permission = Notification.permission;
18133        
18134        if (permission === 'denied') {
18135            this.updateUI('blocked');
18136        } else if (permission === 'granted') {
18137            // Check if already subscribed
18138            const isSubscribed = await this.checkSubscriptionStatus();
18139            this.updateUI(isSubscribed ? 'subscribed' : 'not-subscribed');
18140        } else {
18141            // Default: not subscribed yet
18142            this.updateUI('not-subscribed');
18143            this.showInfoBar();
18144        }
18145    },
18146    
18147    /**
18148     * Check if user is logged in
18149     */
18150    isUserLoggedIn() {
18151        const metaTag = document.querySelector('meta[name="user-logged-in"]');
18152        return metaTag ? metaTag.getAttribute('content') === 'true' : false;
18153    },
18154    
18155    /**
18156     * Check if browser supports push notifications
18157     */
18158    isSupported() {
18159        return (
18160            'serviceWorker' in navigator &&
18161            'PushManager' in window &&
18162            'Notification' in window
18163        );
18164    },
18165    
18166    /**
18167     * Load VAPID public key from server
18168     */
18169    async loadVapidKey() {
18170        try {
18171            const response = await fetch('/api/chat/notifications/vapid_key', {
18172                method: 'GET',
18173                headers: {
18174                    'Accept': 'application/json'
18175                }
18176            });
18177            
18178            if (!response.ok) {
18179                return false;
18180            }
18181            
18182            const data = await response.json();
18183            if (data.success && data.data?.public_key) {
18184                this.vapidPublicKey = data.data.public_key;
18185                return true;
18186            }
18187            
18188            return false;
18189            
18190        } catch (error) {
18191            console.error('[ChatNotification] Error loading VAPID key:', error);
18192            return false;
18193        }
18194    },
18195    
18196    /**
18197     * Register service worker
18198     */
18199    async registerServiceWorker() {
18200        if (!('serviceWorker' in navigator)) return null;
18201        
18202        try {
18203            const registration = await navigator.serviceWorker.register('/sw-push.js', {
18204                scope: '/'
18205            });
18206            return registration;
18207        } catch (error) {
18208            return null;
18209        }
18210    },
18211    
18212    /**
18213     * Check subscription status from server
18214     */
18215    async checkSubscriptionStatus() {
18216        try {
18217            const response = await fetch('/api/chat/notifications/status', {
18218                method: 'GET',
18219                credentials: 'include',
18220                headers: {
18221                    'Accept': 'application/json'
18222                }
18223            });
18224            
18225            if (!response.ok) {
18226                return false;
18227            }
18228            
18229            const data = await response.json();
18230            return data.data?.subscribed || false;
18231            
18232        } catch (error) {
18233            console.error('[ChatNotification] Error checking subscription:', error);
18234            return false;
18235        }
18236    },
18237    
18238    /**
18239     * Update UI based on status
18240     */
18241    updateUI(status) {
18242        this.status = status;
18243        const btn = document.getElementById('chatNotifToggle');
18244        
18245        if (!btn) {
18246            // Retry after a short delay (button might not be in DOM yet)
18247            setTimeout(() => this.updateUI(status), 100);
18248            return;
18249        }
18250        
18251        btn.setAttribute('data-status', status);
18252        
18253        // Show/hide button based on support
18254        if (status === 'not-supported') {
18255            btn.style.display = 'none';
18256        } else {
18257            btn.style.display = 'block';
18258        }
18259        
18260        // Update SVG icon (bell vs bell-slash)
18261        const svg = btn.querySelector('svg');
18262        if (svg) {
18263            if (status === 'subscribed') {
18264                // Bell icon (active)
18265                svg.innerHTML = '<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/>';
18266            } else {
18267                // Bell slash icon (inactive/blocked)
18268                svg.innerHTML = '<path d="M20 18.69L7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11h-.01c-.01 0-.01 0-.02.01-.23.09-.46.2-.68.31 0 0-.01 0-.01.01L18 14.68z"/>';
18269            }
18270        }
18271        
18272        // Update tooltip
18273        const tooltips = {
18274            'not-subscribed': 'Klik untuk aktifkan notifikasi',
18275            'subscribed': 'Notifikasi aktif',
18276            'blocked': 'Notifikasi diblokir'
18277        };
18278        
18279        if (tooltips[status]) {
18280            btn.title = tooltips[status];
18281        }
18282        
18283        // Hide info bar if subscribed or blocked
18284        if (status === 'subscribed' || status === 'blocked') {
18285            this.hideInfoBar();
18286        }
18287    },
18288    
18289    /**
18290     * Show info bar (if not dismissed)
18291     */
18292    showInfoBar() {
18293        if (localStorage.getItem('chat_notif_info_dismissed')) {
18294            return;
18295        }
18296        
18297        const infoBar = document.getElementById('chatNotifInfoBar');
18298        const popupBody = document.getElementById('chat-popup')?.querySelector('.chat-popup-body');
18299        
18300        if (infoBar) {
18301            infoBar.style.display = 'flex';
18302
18303            // Bind dismiss button here so it's always attached when bar is visible
18304            const btn = infoBar.querySelector('#chatNotifDismissBtn');
18305            if (btn && !btn._dismissBound) {
18306                btn._dismissBound = true;
18307                btn.addEventListener('click', () => {
18308                    infoBar.style.display = 'none';
18309                    localStorage.setItem('chat_notif_info_dismissed', '1');
18310                    if (popupBody) popupBody.classList.remove('has-info-bar');
18311                });
18312            }
18313        }
18314        
18315        if (popupBody) {
18316            popupBody.classList.add('has-info-bar');
18317        }
18318    },
18319    
18320    /**
18321     * Hide info bar
18322     */
18323    hideInfoBar() {
18324        const infoBar = document.getElementById('chatNotifInfoBar');
18325        const popupBody = document.getElementById('chat-popup')?.querySelector('.chat-popup-body');
18326        
18327        if (infoBar) {
18328            infoBar.style.display = 'none';
18329        }
18330        
18331        if (popupBody) {
18332            popupBody.classList.remove('has-info-bar');
18333        }
18334    },
18335    
18336    /**
18337     * Dismiss info bar permanently
18338     */
18339    dismissInfo() {
18340        this.hideInfoBar();
18341        localStorage.setItem('chat_notif_info_dismissed', '1');
18342    },
18343    
18344    /**
18345     * Handle bell icon click
18346     */
18347    async handleToggleClick() {
18348        if (this.status === 'not-subscribed') {
18349            await this.subscribe();
18350        } else if (this.status === 'subscribed') {
18351            await this.unsubscribe();
18352        } else if (this.status === 'blocked') {
18353            this.showUnblockHelp();
18354        }
18355    },
18356    
18357    /**
18358     * Subscribe to push notifications
18359     */
18360    async subscribe() {
18361        try {
18362            // Request permission
18363            const permission = await Notification.requestPermission();
18364            
18365            if (permission === 'denied') {
18366                this.updateUI('blocked');
18367                this.showToast('Notifikasi diblokir', 'error');
18368                return;
18369            }
18370            
18371            if (permission !== 'granted') {
18372                return;
18373            }
18374            
18375            // Get service worker registration
18376            const registration = await navigator.serviceWorker.ready;
18377            
18378            // Subscribe to push
18379            const subscription = await registration.pushManager.subscribe({
18380                userVisibleOnly: true,
18381                applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicKey)
18382            });
18383            
18384            // Send subscription to server
18385            const response = await fetch('/api/chat/notifications/subscribe', {
18386                method: 'POST',
18387                credentials: 'include',
18388                headers: {
18389                    'Content-Type': 'application/json',
18390                    'Accept': 'application/json'
18391                },
18392                body: JSON.stringify(subscription.toJSON())
18393            });
18394            
18395            if (!response.ok) {
18396                throw new Error('Failed to save subscription');
18397            }
18398            
18399            const result = await response.json();
18400            
18401            this.subscription = subscription;
18402            this.updateUI('subscribed');
18403            this.hideInfoBar();
18404            this.showToast('Notifikasi berhasil diaktifkan!', 'success');
18405            
18406            // Send test notification
18407            setTimeout(() => this.sendTestNotification(), 1000);
18408            
18409        } catch (error) {
18410            console.error('[ChatNotification] Subscribe error:', error);
18411            this.showToast('Gagal mengaktifkan notifikasi', 'error');
18412        }
18413    },
18414    
18415    /**
18416     * Unsubscribe from push notifications
18417     */
18418    async unsubscribe() {
18419        try {
18420            // Confirm first
18421            if (!confirm('Nonaktifkan notifikasi chat?')) {
18422                return;
18423            }
18424            
18425            // Get current subscription
18426            const registration = await navigator.serviceWorker.ready;
18427            const subscription = await registration.pushManager.getSubscription();
18428            
18429            if (subscription) {
18430                // Unsubscribe from browser
18431                await subscription.unsubscribe();
18432                
18433                // Tell server
18434                await fetch('/api/chat/notifications/unsubscribe', {
18435                    method: 'POST',
18436                    credentials: 'include',
18437                    headers: {
18438                        'Content-Type': 'application/json',
18439                        'Accept': 'application/json'
18440                    },
18441                    body: JSON.stringify(subscription.toJSON())
18442                });
18443            }
18444            
18445            this.subscription = null;
18446            this.updateUI('not-subscribed');
18447            this.showInfoBar();
18448            this.showToast('Notifikasi dinonaktifkan', 'info');
18449            
18450        } catch (error) {
18451            console.error('[ChatNotification] Unsubscribe error:', error);
18452            this.showToast('Gagal menonaktifkan notifikasi', 'error');
18453        }
18454    },
18455    
18456    /**
18457     * Send test notification
18458     */
18459    async sendTestNotification() {
18460        try {
18461            const response = await fetch('/api/chat/notifications/send_test', {
18462                method: 'POST',
18463                credentials: 'include',
18464                headers: {
18465                    'Accept': 'application/json'
18466                }
18467            });
18468            
18469            if (response.ok) {
18470                // Test notification sent successfully
18471            }
18472        } catch (error) {
18473            // Test notification failed silently
18474        }
18475    },
18476    
18477    /**
18478     * Show unblock help modal/message
18479     */
18480    showUnblockHelp() {
18481        const message = `
18482            Notifikasi diblokir oleh browser.\n\n
18483            Untuk mengaktifkan:\n
18484            1. Klik ikon gembok/info di address bar\n
18485            2. Cari "Notifications" atau "Notifikasi"\n
18486            3. Ubah menjadi "Allow" atau "Izinkan"\n
18487            4. Refresh halaman ini
18488        `.trim().replace(/\s+/g, ' ');
18489        
18490        alert(message);
18491    },
18492    
18493    /**
18494     * Show toast notification
18495     */
18496    showToast(message, type = 'info') {
18497        // Simple toast implementation
18498        const toast = document.createElement('div');
18499        toast.className = `chat-toast chat-toast-${type}`;
18500        toast.textContent = message;
18501        toast.style.cssText = `
18502            position: fixed;
18503            bottom: 80px;
18504            right: 20px;
18505            background: ${type === 'success' ? '#4caf50' : type === 'error' ? '#f44336' : '#2196f3'};
18506            color: white;
18507            padding: 12px 20px;
18508            border-radius: 4px;
18509            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
18510            z-index: 10000;
18511            animation: slideInUp 0.3s ease;
18512        `;
18513        
18514        document.body.appendChild(toast);
18515        
18516        setTimeout(() => {
18517            toast.style.animation = 'slideOutDown 0.3s ease';
18518            setTimeout(() => toast.remove(), 300);
18519        }, 3000);
18520    },
18521    
18522    /**
18523     * Convert VAPID key to Uint8Array
18524     */
18525    urlBase64ToUint8Array(base64String) {
18526        const padding = '='.repeat((4 - base64String.length % 4) % 4);
18527        const base64 = (base64String + padding)
18528            .replace(/\-/g, '+')
18529            .replace(/_/g, '/');
18530        
18531        const rawData = window.atob(base64);
18532        const outputArray = new Uint8Array(rawData.length);
18533        
18534        for (let i = 0; i < rawData.length; ++i) {
18535            outputArray[i] = rawData.charCodeAt(i);
18536        }
18537        return outputArray;
18538    },
18539    
18540    /**
18541     * Check if push notification is currently active
18542     * @returns {Promise<boolean>}
18543     */
18544    async isPushActive() {
18545        try {
18546            // Check browser subscription
18547            if (!('serviceWorker' in navigator)) return false;
18548            
18549            const registration = await navigator.serviceWorker.getRegistration();
18550            if (!registration) return false;
18551            
18552            const subscription = await registration.pushManager.getSubscription();
18553            if (!subscription) return false;
18554            
18555            // Also check server status
18556            const serverStatus = await this.checkSubscriptionStatus();
18557            
18558            return serverStatus;
18559        } catch (error) {
18560            return false;
18561        }
18562    },
18563    
18564    /**
18565     * Show logout confirmation if push notification is active
18566     * Call this before logout action
18567     * @returns {Promise<boolean>} true if user confirms logout, false if cancelled
18568     */
18569    async confirmLogoutWithPushNotification() {
18570        const isActive = await this.isPushActive();
18571        
18572        if (!isActive) {
18573            // No active push, proceed with logout
18574            return true;
18575        }
18576        
18577        // Show modal confirmation using modal.css
18578        return new Promise((resolve) => {
18579            // Create modal HTML
18580            const modalHTML = `
18581                <div class="modal-overlay" id="logoutConfirmModal">
18582                    <div class="modal-dialog">
18583                        <div class="modal-header">
18584                            <h3 class="modal-title">
18585                                <span class="modal-icon warning">⚠️</span>
18586                                Konfirmasi Logout
18587                            </h3>
18588                        </div>
18589                        <div class="modal-body">
18590                            <p class="modal-message">
18591                                Anda memiliki <strong>notifikasi push yang aktif</strong>.<br><br>
18592                                Setelah logout, Anda tidak akan menerima notifikasi chat lagi di perangkat ini.<br><br>
18593                                Apakah Anda yakin ingin melanjutkan logout?
18594                            </p>
18595                        </div>
18596                        <div class="modal-actions" style="padding: 0 20px 20px;">
18597                            <button class="modal-btn modal-btn-secondary" id="logoutCancelBtn">
18598                                Batal
18599                            </button>
18600                            <button class="modal-btn modal-btn-danger" id="logoutConfirmBtn">
18601                                Lanjutkan Logout
18602                            </button>
18603                        </div>
18604                    </div>
18605                </div>
18606            `;
18607            
18608            // Append to body
18609            const modalContainer = document.createElement('div');
18610            modalContainer.innerHTML = modalHTML;
18611            document.body.appendChild(modalContainer.firstElementChild);
18612            
18613            const modal = document.getElementById('logoutConfirmModal');
18614            const confirmBtn = document.getElementById('logoutConfirmBtn');
18615            const cancelBtn = document.getElementById('logoutCancelBtn');
18616            
18617            // Show modal with animation
18618            requestAnimationFrame(() => {
18619                modal.classList.add('show');
18620            });
18621            
18622            // Handle confirm
18623            const handleConfirm = () => {
18624                cleanup();
18625                resolve(true);
18626            };
18627            
18628            // Handle cancel
18629            const handleCancel = () => {
18630                cleanup();
18631                resolve(false);
18632            };
18633            
18634            // Cleanup function
18635            const cleanup = () => {
18636                modal.classList.remove('show');
18637                setTimeout(() => {
18638                    modal.remove();
18639                }, 200);
18640                confirmBtn.removeEventListener('click', handleConfirm);
18641                cancelBtn.removeEventListener('click', handleCancel);
18642                modal.removeEventListener('click', handleBackdropClick);
18643            };
18644            
18645            // Handle backdrop click
18646            const handleBackdropClick = (e) => {
18647                if (e.target === modal) {
18648                    handleCancel();
18649                }
18650            };
18651            
18652            // Add event listeners
18653            confirmBtn.addEventListener('click', handleConfirm);
18654            cancelBtn.addEventListener('click', handleCancel);
18655            modal.addEventListener('click', handleBackdropClick);
18656        });
18657    }
18658};
18659
18660// Add CSS animations
18661const chatNotifStyle = document.createElement('style');
18662chatNotifStyle.textContent = `
18663    @keyframes slideInUp {
18664        from {
18665            transform: translateY(100%);
18666            opacity: 0;
18667        }
18668        to {
18669            transform: translateY(0);
18670            opacity: 1;
18671        }
18672    }
18673    
18674    @keyframes slideOutDown {
18675        from {
18676            transform: translateY(0);
18677            opacity: 1;
18678        }
18679        to {
18680            transform: translateY(100%);
18681            opacity: 0;
18682        }
18683    }
18684`;
18685document.head.appendChild(chatNotifStyle);
18686
18687// Auto-initialize when DOM is ready
18688if (document.readyState === 'loading') {
18689    document.addEventListener('DOMContentLoaded', () => ChatNotification.init());
18690} else {
18691    ChatNotification.init();
18692}
18693
18694// Expose to window for access from other scripts
18695window.ChatNotification = ChatNotification;
18696
18697</script>
18697  
18698    <!-- Footer Collapse Toggle Script -->
18699  
18699<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
18700  document.addEventListener('DOMContentLoaded', function() {
18701    const ftTop = document.getElementById('ftTop');
18702    const expandBtn = document.getElementById('ftExpandBtn');
18703    
18704    if (ftTop && expandBtn) {
18705      const expandText = expandBtn.querySelector('.expand-text');
18706      
18707      expandBtn.addEventListener('click', function() {
18708        ftTop.classList.toggle('expanded');
18709        const isExpanded = ftTop.classList.contains('expanded');
18710        if (expandText) {
18711          expandText.textContent = isExpanded ? 'Sembunyikan' : 'Selengkapnya';
18712        }
18713      });
18714    }
18715  });
18716  </script>
18716
18717  </footer>
18718
18719<!-- Compare Sidebar Overlay -->
18720<div class="compare-overlay" id="compareOverlay"></div>
18721
18722<!-- Floating Compare Badge (Sticky) -->
18723<button class="compare-floating-badge" id="compareFloatingBadge" aria-label="Lihat produk yang dibandingkan" style="display:none;">
18724  <svg class="cfb-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
18725    <path d="M9.01 14H2v2h7.01v3L13 15l-3.99-4v3zm5.98-1v-3H22V8h-7.01V5L11 9l3.99 4z"/>
18726  </svg>
18727  <span class="cfb-text">Bandingkan</span>
18728  <span class="cfb-count" id="cfbCount">0</span>
18729</button>
18730
18731<!-- AI Comparison Modal -->
18732<div class="ai-compare-modal" id="aiCompareModal">
18733  <div class="modal-backdrop"></div>
18734  <div class="modal-container">
18735    <div class="modal-header">
18736      <h3 class="modal-title">
18737        <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
18738          <path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z"/>
18739        </svg>
18740        Analisis AI: Perbandingan Produk
18741      </h3>
18742      <button class="modal-close" id="aiCompareModalClose" aria-label="Tutup">
18743        <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18744          <path d="M18.3 5.7a1 1 0 0 0-1.4 0L12 10.6 7.1 5.7a1 1 0 1 0-1.4 1.4L10.6 12l-4.9 4.9a1 1 0 0 0 1.4 1.4L12 13.4l4.9 4.9a1 1 0 0 0 1.4-1.4L13.4 12l4.9-4.9a1 1 0 0 0 0-1.4z"/>
18745        </svg>
18746      </button>
18747    </div>
18748    
18749    <div class="modal-content">
18750      <!-- Loading State -->
18751      <div class="ai-loading" id="aiLoading">
18752        <div class="loading-animation">
18753          <div class="typing-indicator">
18754            <span></span>
18755            <span></span>
18756            <span></span>
18757          </div>
18758          <p>AI sedang menganalisis produk...</p>
18759          <small>Proses ini membutuhkan waktu 10-30 detik</small>
18760        </div>
18761      </div>
18762      
18763      <!-- Error State -->
18764      <div class="ai-error" id="aiError" hidden>
18765        <div class="error-icon">
18766          <svg width="48" height="48" viewBox="0 0 24 24" fill="#ef4444">
18767            <path d="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"/>
18768          </svg>
18769        </div>
18770        <h4>Analisis AI Gagal</h4>
18771        <p id="aiErrorMessage">Terjadi kesalahan saat memproses analisis.</p>
18772        <button class="btn btn-outline-primary" id="aiRetry">Coba Lagi</button>
18773      </div>
18774      
18775      <!-- Analysis Results -->
18776      <div class="ai-results" id="aiResults" hidden>
18777        <!-- Summary Section -->
18778        <div class="ai-summary-card">
18779          <h4>
18780            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18781              <path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,16.5L6.5,12L7.91,10.59L11,13.67L16.59,8.09L18,9.5L11,16.5Z"/>
18782            </svg>
18783            Ringkasan Analisis
18784          </h4>
18785          <p id="aiSummary" class="summary-text"></p>
18786          <div class="winner-badge" id="winnerBadge">
18787            <strong>Rekomendasi Terbaik:</strong> <span id="winnerProduct"></span>
18788            <small id="winnerReason"></small>
18789          </div>
18790        </div>
18791        
18792        <!-- Comparison Table -->
18793        <div class="ai-comparison-table">
18794          <h4>
18795            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18796              <path d="M3,3H21V5H3V3M3,7H21V9H3V7M3,11H21V13H3V11M3,15H21V17H3V15M3,19H21V21H3V19Z"/>
18797            </svg>
18798            Perbandingan Detail
18799          </h4>
18800          <div id="comparisonTable" class="comparison-grid"></div>
18801        </div>
18802        
18803        <!-- Detailed Analysis -->
18804        <div class="ai-detailed-analysis">
18805          <h4>
18806            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18807              <path d="M14,2A8,8 0 0,1 22,10A8,8 0 0,1 14,18A8,8 0 0,1 6,10A8,8 0 0,1 14,2M14,4A6,6 0 0,0 8,10A6,6 0 0,0 14,16A6,6 0 0,0 20,10A6,6 0 0,0 14,4M14,6A4,4 0 0,1 18,10A4,4 0 0,1 14,14A4,4 0 0,1 10,10A4,4 0 0,1 14,6M14,8A2,2 0 0,0 12,10A2,2 0 0,0 14,12A2,2 0 0,0 16,10A2,2 0 0,0 14,8Z"/>
18808            </svg>
18809            Analisis Mendalam
18810          </h4>
18811          <div id="detailedAnalysis" class="products-analysis"></div>
18812        </div>
18813        
18814        <!-- Recommendations -->
18815        <div class="ai-recommendations">
18816          <h4>
18817            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18818              <path d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"/>
18819            </svg>
18820            Rekomendasi Berdasarkan Kebutuhan
18821          </h4>
18822          <div class="recommendations-grid">
18823            <div class="recommendation-card">
18824              <h5>💰 Budget Conscious</h5>
18825              <p id="budgetRecommendation"></p>
18826            </div>
18827            <div class="recommendation-card">
18828              <h5>⚡ Performance Focused</h5>
18829              <p id="performanceRecommendation"></p>
18830            </div>
18831            <div class="recommendation-card">
18832              <h5>⚖️ Balanced Choice</h5>
18833              <p id="balancedRecommendation"></p>
18834            </div>
18835          </div>
18836        </div>
18837        
18838        <!-- Buying Tips -->
18839        <div class="ai-tips">
18840          <h4>
18841            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18842              <path d="M11,18H13V16H11V18M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6
18842M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3Z"/>
18843            </svg>
18844            Tips Pembelian
18845          </h4>
18846          <ul id="buyingTips" class="tips-list"></ul>
18847        </div>
18848      </div>
18849    </div>
18850  </div>
18851</div>
18852
18853<!-- Cart Login Modal -->
18854<div class="modal-overlay" id="cartLoginModal">
18855  <div class="modal-dialog">
18856    <div class="modal-header">
18857      <h3 class="modal-title">
18858        <div class="modal-icon info">
18859          <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18860            <path d="M17,18A2,2 0 0,1 19,20A2,2 0 0,1 17,22C15.89,22 15,21.1 15,20C15,18.89 15.89,18 17,18M1,2H4.27L5.21,4H20A1,1 0 0,1 21,5C21,5.17 20.95,5.34 20.88,5.5L17.3,11.97C16.96,12.58 16.3,13 15.55,13H8.1L7.2,14.63L7.17,14.75A0.25,0.25 0 0,0 7.42,15H19V17H7C5.89,17 5,16.1 5,15C5,14.65 5.09,14.32 5.24,14.04L6.6,11.59L3,4H1V2M7,18A2,2 0 0,1 9,20A2,2 0 0,1 7,22C5.89,22 5,21.1 5,20C5,18.89 5.89,18 7,18M16,11L18.78,6H6.14L8.5,11H16Z"/>
18861          </svg>
18862        </div>
18863        Login untuk Melanjutkan
18864      </h3>
18865    </div>
18866    <div class="modal-body">
18867      <p class="modal-message" style="margin-bottom: 16px;">Silakan login atau daftar untuk mengakses keranjang belanja dan menyelesaikan pesanan Anda.</p>
18868      <div style="background: #f8fafc; border-radius: 8px; padding: 12px; margin-bottom: 20px;">
18869        <p style="font-size: 14px; color: #646947; margin: 0; line-height: 1.6;">
18870          <strong style="color: #2a2a0f;">Keuntungan dengan akun:</strong><br>
18871          ✓ Simpan keranjang belanja Anda<br>
18872          ✓ Lacak status pesanan secara real-time<br>
18873          ✓ Checkout lebih cepat dengan data tersimpan<br>
18874          ✓ Dapatkan notifikasi promo & diskon eksklusif
18875        </p>
18876      </div>
18877      <div class="modal-actions">
18878        <button class="modal-btn modal-btn-secondary" id="cartLoginCancel">Batal</button>
18879        <button class="modal-btn modal-btn-primary" id="cartLoginConfirm" style="background: #ff6b35; border-color: #ff6b35;">Login</button>
18880        <button class="modal-btn modal-btn-primary" id="cartRegisterBtn" style="background: #f7931e; border-color: #f7931e;">Daftar</button>
18881      </div>
18882    </div>
18883  </div>
18884</div>
18885
18886
18887<script data-inlined="app.js">
18888(function initLiveSearch(){
18889  function setup() {
18890    const searchInput = document.getElementById('searchInput');
18891    const suggest = document.getElementById('searchSuggest');
18892    const searchBtn = document.getElementById('searchBtn');
18893    const aiToggleBtn = document.getElementById('aiToggleBtn');
18894    
18895    if (!searchInput || !suggest || !aiToggleBtn) {
18896      console.log('[LiveSearch] Elemen belum ada, retry...');
18897      return false;
18898    }
18899
18900    let debounceTimer = null;
18901    let lastQuery = '';
18902    let currentIndex = -1;
18903    let currentAbortController = null;
18904    let isAIModeEnabled = false; // AI mode OFF by default
18905    let isAIProcessing = false; // Flag to prevent closing suggest box during AI processing
18906    
18907    // Trending cache
18908    let trendingCache = null;
18909    let trendingLoading = false;
18910    
18911    // Trending products cache
18912    let trendingProductsCache = null;
18913    let trendingProductsLoading = false;
18914    
18915    const MIN_LEN = 2;
18916    const DEBOUNCE = 300;
18917    const AI_TIMEOUT = 15000; // 15 seconds timeout for AI endpoint
18918
18919    // Load AI mode preference from localStorage
18920    const savedAIMode = localStorage.getItem('plazait_ai_search_mode');
18921    if (savedAIMode === 'enabled') {
18922      isAIModeEnabled = false;
18923      aiToggleBtn.classList.remove('active');
18924    }
18925    
18926    // Update placeholder based on AI mode
18927    function updatePlaceholder() {
18928      if (isAIModeEnabled) {
18929        searchInput.placeholder = '🤖 Coba: "laptop HP core i3 untuk kerja" atau "tinta epson l3210"';
18930      } else {
18931        // Static placeholder for typing animation
18932        searchInput.placeholder = '';
18933      }
18934    }
18935    
18936    // Typing animation for placeholder
18937    let typingTimeout = null;
18938    let currentProductIndex = 0;
18939    let currentCharIndex = 0;
18940    let isTyping = false;
18941    let isDeleting = false;
18942    
18943    const popularProducts = [
18944      'Oppo A6',
18945      'Asus Ryzen 3',
18946      'Starlink V4',
18947      'HP Victus',
18948      'Lenovo RTX 5060',
18949      'ROG Strix',
18950      'iPhone 13',
18951      'Xpad 20',
18952      'Xiaomi 14',
18953      'Epson L3210',
18954      'Realme C85',
18955      'Brother T720',
18956      'Mouse Logitech'
18957    ];
18958    
18959    function typeWriter() {
18960      if (isAIModeEnabled || document.activeElement === searchInput) {
18961        return; // Skip typing if AI mode is on or input is focused
18962      }
18963      
18964      const currentProduct = popularProducts[currentProductIndex];
18965      
18966      if (!isDeleting) {
18967        // Typing forward
18968        if (currentCharIndex < currentProduct.length) {
18969          searchInput.placeholder = currentProduct.substring(0, currentCharIndex + 1);
18970          currentCharIndex++;
18971          typingTimeout = setTimeout(typeWriter, 100); // Typing speed
18972        } else {
18973          // Finished typing, wait before deleting
18974          typingTimeout = setTimeout(() => {
18975            isDeleting = true;
18976            typeWriter();
18977          }, 2000); // Pause before deleting
18978        }
18979      } else {
18980        // Deleting backward
18981        if (currentCharIndex > 0) {
18982          searchInput.placeholder = currentProduct.substring(0, currentCharIndex - 1);
18983          currentCharIndex--;
18984          typingTimeout = setTimeout(typeWriter, 50); // Deleting speed (faster)
18985        } else {
18986          // Finished deleting, move to next product
18987          isDeleting = false;
18988          currentProductIndex = (currentProductIndex + 1) % popularProducts.length;
18989          typingTimeout = setTimeout(typeWriter, 500); // Pause before typing next
18990        }
18991      }
18992    }
18993    
18994    function startTypingAnimation() {
18995      if (!isAIModeEnabled && !typingTimeout) {
18996        currentCharIndex = 0;
18997        isDeleting = false;
18998        typeWriter();
18999      }
19000    }
19001    
19002    function stopTypingAnimation() {
19003      if (typingTimeout) {
19004        clearTimeout(typingTimeout);
19005        typingTimeout = null;
19006      }
19007    }
19008    
19009    // Set initial placeholder
19010    updatePlaceholder();
19011    
19012    // Start typing animation for normal mode
19013    if (!isAIModeEnabled) {
19014      startTypingAnimation();
19015    }
19016
19017    function esc(str){
19018      if (str === null || str === undefined) return '';
19019      return String(str)
19020        .replace(/&/g,'&amp;')
19021        .replace(/</g,'&lt;')
19022        .replace(/>/g,'&gt;')
19023        .replace(/"/g,'&quot;')
19024        .replace(/'/g,'&#39;');
19025    }
19026
19027    function clearSuggest(){
19028      suggest.innerHTML = '';
19029      suggest.hidden = true;
19030      suggest.classList.remove('open', 'trending-mode');
19031      currentIndex = -1;
19032    }
19033
19034    // ==================== TRENDING FUNCTIONS ====================
19035    
19036    async function fetchTrending() {
19037      if (trendingCache) return trendingCache;
19038      if (trendingLoading) return null;
19039      
19040      trendingLoading = true;
19041      
19042      try {
19043        const res = await fetch('/api/trending_searches.php?timeframe=7d&limit=8');
19044        const data = await res.json();
19045        
19046        if (data.ok && Array.isArray(data.data)) {
19047          trendingCache = data.data;
19048          console.log('[Trending] Loaded', trendingCache.length, 'items');
19049          return trendingCache;
19050        }
19051      } catch (err) {
19052        console.error('[Trending] Fetch error:', err);
19053      } finally {
19054        trendingLoading = false;
19055      }
19056      
19057      return null;
19058    }
19059    
19060    async function fetchTrendingProducts() {
19061      if (trendingProductsCache) return trendingProductsCache;
19062      if (trendingProductsLoading) return null;
19063      
19064      trendingProductsLoading = true;
19065      
19066      try {
19067        const res = await fetch('/api/trending_products.php?timeframe=7d&limit=6');
19068        const data = await res.json();
19069        
19070        if (data.ok && Array.isArray(data.data)) {
19071          trendingProductsCache = data.data;
19072          console.log('[Trending Products] Loaded', trendingProductsCache.length, 'items');
19073          return trendingProductsCache;
19074        }
19075      } catch (err) {
19076        console.error('[Trending Products] Fetch error:', err);
19077      } finally {
19078        trendingProductsLoading = false;
19079      }
19080      
19081      return null;
19082    }
19083    
19084    function renderTrendingSuggestions(trends, products) {
19085      if ((!trends || trends.length === 0) && (!products || products.length === 0)) {
19086        clearSuggest();
19087        return;
19088      }
19089      
19090      let html = `<div class="trending-container">`;
19091      
19092      // Left column: Trending searches
19093      if (trends && trends.length > 0) {
19094        html += `
19095          <div class="trending-column trending-searches">
19096            <div class="trending-header">
19097              <span class="trending-icon">🔥</span>
19098              <span class="trending-title">Pencarian Trending</span>
19099            </div>
19100        `;
19101        
19102        html += trends.map((t, idx) => {
19103          const categoryHint = t.category_hint ? `<span class="trending-category">${esc(t.category_hint)}</span>` : '';
19104          return `
19105            <div class="trending-item" data-query="${esc(t.query)}" data-index="${idx}">
19106              <span class="trending-rank">
19107                <img src="/assets/img/uptrend.image" alt="trending" width="16" height="16" style="vertical-align: middle;">
19108              </span>
19109              <span class="trending-query">${esc(t.query)}</span>
19110              ${categoryHint}
19111            </div>
19112          `;
19113        }).join('');
19114        
19115        html += `</div>`;
19116      }
19117      
19118      // Right column: Trending products
19119      if (products && products.length > 0) {
19120        html += `
19121          <div class="trending-column trending-products">
19122            <div class="trending-header">
19123              <span class="trending-icon">⭐</span>
19124              <span class="trending-title">Produk Populer</span>
19125            </div>
19126        `;
19127        
19128        html += products.map((p, idx) => {
19129          return `
19130            <div class="trending-product-item" data-product-id="${esc(p.id)}" data-slug="${esc(p.slug)}" data-query="${esc(p.top_query || '')}" data-index="${idx}">
19131              <img src="${esc(p.image)}" alt="${esc(p.name)}" class="trending-product-thumb">
19132              <div class="trending-product-info">
19133                <div class="trending-product-name">${esc(p.name)}</div>
19134                <div class="trending-product-price">${esc(p.price_label)}</div>
19135              </div>
19136            </div>
19137          `;
19138        }).join('');
19139        
19140        html += `</div>`;
19141      }
19142      
19143      html += `</div>`;
19144      
19145      suggest.innerHTML = html;
19146      suggest.hidden = false;
19147      suggest.classList.add('open', 'trending-mode');
19148      suggest.style.display = 'block';
19149      
19150      console.log('[Trending] Rendered', trends?.length || 0, 'searches and', products?.length || 0, 'products');
19151    }
19152    
19153    function trackSearchClick(query, productId) {
19154      fetch('/api/track_search_click.php', {
19155        method: 'POST',
19156        headers: { 'Content-Type': 'application/json' },
19157        body: JSON.stringify({
19158          query: query,
19159          product_id: productId,
19160          click_type: 'suggestion'
19161        })
19162      }).catch(() => {}); // silent fail
19163    }
19164    
19165    function trackTrendingClick(query) {
19166      fetch('/api/track_search_click.php', {
19167        method: 'POST',
19168        headers: { 'Content-Type': 'application/json' },
19169        body: JSON.stringify({
19170          query: query,
19171          click_type: 'trending'
19172        })
19173      }).catch(() => {});
19174    }
19175    
19176    function trackTrendingProductClick(productId, query) {
19177      fetch('/api/track_search_click.php', {
19178        method: 'POST',
19179        headers: { 'Content-Type': 'application/json' },
19180        body: JSON.stringify({
19181          query: query || 'trending_product',
19182          product_id: productId,
19183          click_type: 'trending_product'
19184        })
19185      }).catch(() => {});
19186    }
19187    
19188    function trackSearchSubmit(query) {
19189      fetch('/api/track_search_click.php', {
19190        method: 'POST',
19191        headers: { 'Content-Type': 'application/json' },
19192        body: JSON.stringify({
19193          query: query,
19194          click_type: 'submit'
19195        })
19196      }).catch(() => {});
19197    }
19198    
19199    function logFinalSearch(query) {
19200      // Log the final search to analytics (not during typing)
19201      fetch('/api/search_products.php?q=' + encodeURIComponent(query) + '&log=1', {
19202        headers: { 'Accept': 'application/json' }
19203      }).catch(() => {});
19204    }
19205
19206    // ==================== END TRENDING FUNCTIONS ====================
19207
19208    function clearSuggest(){
19209      suggest.innerHTML = '';
19210      suggest.hidden = true;
19211      suggest.classList.remove('open', 'trending-mode');
19212      currentIndex = -1;
19213    }
19214
19215    // AI Toggle Handler
19216    aiToggleBtn.addEventListener('click', (e) => {
19217      e.preventDefault();
19218      e.stopPropagation();
19219      
19220      isAIModeEnabled = !isAIModeEnabled;
19221      
19222      if (isAIModeEnabled) {
19223        aiToggleBtn.classList.add('active');
19224        localStorage.setItem('plazait_ai_search_mode', 'enabled');
19225        updatePlaceholder(); // Update placeholder text
19226        stopTypingAnimation(); // Stop typing animation in AI mode
19227        
19228        // Show AI prompt if there's a query
19229        const query = searchInput.value.trim();
19230        if (query.length >= MIN_LEN) {
19231          showAISearchPrompt(query);
19232        } else {
19233          showAIActivationMessage();
19234        }
19235      } else {
19236        aiToggleBtn.classList.remove('active');
19237        localStorage.setItem('plazait_ai_search_mode', 'disabled');
19238        updatePlaceholder(); // Update placeholder text
19239        startTypingAnimation(); // Start typing animation in normal mode
19240        clearSuggest();
19241        
19242        // Switch to normal search if there's a query
19243        const query = searchInput.value.trim();
19244        if (query.length >= MIN_LEN) {
19245          performSearch(query);
19246        }
19247      }
19248    });
19249
19250    function showAIActivationMessage() {
19251      suggest.innerHTML = `
19252        <div class="ai-progress-container">
19253          <div class="ai-progress-header">
19254            <div class="ai-progress-icon">✨</div>
19255            <div class="ai-progress-text">
19256              <div class="ai-progress-title">AI Search Mode Activated!</div>
19257              <div class="ai-progress-subtitle">Ketik query Anda untuk pencarian cerdas</div>
19258            </div>
19259          </div>
19260        </div>
19261      `;
19262      suggest.hidden = false;
19263      suggest.classList.add('open');
19264      suggest.style.display = 'block';
19265    }
19266
19267    function renderAIInsights(insights){
19268      if (!insights) return '';
19269      
19270      // Generate AI summary via backend endpoint
19271      const summary = insights.ai_summary || generateAISummary(insights);
19272      
19273      if (!summary) return '';
19274      
19275      return `
19276        <div class="ai-result-banner">
19277          <div class="ai-result-icon">✨</div>
19278          <div class="ai-result-text">
19279            <div class="ai-result-title">AI Assistant</div>
19280            <div class="ai-result-description">${summary}</div>
19281          </div>
19282        </div>
19283      `;
19284    }
19285    
19286    function generateAISummary(insights) {
19287      // Fallback client-side summary generation if backend doesn't provide ai_summary
19288      const productName = insights.product_name || '';
19289      const categoryPath = insights.category_path || '';
19290      const priceRange = insights.price_range || {};
19291      const intent = insights.intent || '';
19292      
19293      let summary = '';
19294      
19295      // Parse category for natural display
19296      let categoryDisplay = '';
19297      if (categoryPath) {
19298        const categoryParts = categoryPath.split('/');
19299        categoryDisplay = categoryParts[categoryParts.length - 1] || categoryParts[categoryParts.length - 2] || categoryPath;
19300      }
19301      
19302      // Intent-based opening
19303      if (intent === 'cek_stok') {
19304        summary = `Berikut ketersediaan stok untuk <strong>${esc(productName)}</strong>. `;
19305      } else if (intent === 'rekomendasi_produk') {
19306        summary = `Berikut rekomendasi produk <strong>${esc(categoryDisplay || productName)}</strong> yang kami pilihkan untuk Anda. `;
19307      } else {
19308        // Default: cari_harga or general
19309        if (productName && categoryDisplay) {
19310          summary = `Kami menemukan <strong>${esc(productName)}</strong> di kategori ${esc(categoryDisplay)}`;
19311        } else if (productName) {
19312          summary = `Kami menemukan produk <strong>${esc(productName)}</strong>`;
19313        } else if (categoryDisplay) {
19314          summary = `Kami menemukan produk ${esc(categoryDisplay)}`;
19315        }
19316        
19317        // Add price context
19318        if (priceRange.min > 0 && priceRange.max > 0) {
19319          const minPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.min);
19320          const maxPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.max);
19321          summary += ` dengan kisaran harga ${minPrice} - ${maxPrice}`;
19322        } else if (priceRange.min > 0) {
19323          const minPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.min);
19324          summary += ` dengan harga mulai ${minPrice}`;
19325        } else if (priceRange.max > 0) {
19326          const maxPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.max);
19327          summary += ` dengan harga maksimal ${maxPrice}`;
19328        }
19329        
19330        summary += ' yang sesuai dengan kebutuhan Anda.';
19331      }
19332      
19333      return summary || esc(insights.explanation || '');
19334    }
19335
19336    function showAIProgressSteps() {
19337      return `
19338        <div class="ai-progress-container">
19339          <div class="ai-progress-header">
19340            <div class="ai-progress-icon">🤖</div>
19341            <div class="ai-progress-text">
19342              <div class="ai-progress-title">AI sedang menganalisis...</div>
19343              <div class="ai-progress-subtitle">Memproses query Anda dengan kecerdasan buatan</div>
19344            </div>
19345          </div>
19346          <div class="ai-progress-steps">
19347            <div class="ai-progress-step active">
19348              <div class="ai-step-icon">1</div>
19349              <div class="ai-step-text">Memahami maksud pencarian...</div>
19350            </div>
19351            <div class="ai-progress-step pending">
19352              <div class="ai-step-icon">2</div>
19353              <div class="ai-step-text">Mengekstrak parameter pencarian...</div>
19354            </div>
19355            <div class="ai-progress-step pending">
19356              <div class="ai-step-icon">3</div>
19357              <div class="ai-step-text">Mencari produk yang relevan...</div>
19358            </div>
19359          </div>
19360        </div>
19361      `;
19362    }
19363
19364    function updateAIProgressStep(step) {
19365      const steps = suggest.querySelectorAll('.ai-progress-step');
19366      
19367      steps.forEach((stepEl, idx) => {
19368        const icon = stepEl.querySelector('.ai-step-icon');
19369        
19370        if (idx < step) {
19371          stepEl.classList.remove('active', 'pending');
19372          stepEl.classList.add('completed');
19373          icon.textContent = '✓';
19374        } else if (idx === step) {
19375          stepEl.classList.remove('pending', 'completed');
19376          stepEl.classList.add('active');
19377        }
19378      });
19379    }
19380
19381    function renderSuggest(items, aiInsights){
19382      if (!items.length){
19383        clearSuggest();
19384        return;
19385      }
19386      
19387      let html = '';
19388      
19389      // Show AI insights if available
19390      if (aiInsights && aiInsights.explanation) {
19391        html += renderAIInsights(aiInsights);
19392      }
19393      
19394      // Section header for products with hint
19395      html += `
19396        <div class="suggest-section-header">
19397          <div class="suggest-header-main">
19398            <span class="suggest-header-icon">📦</span>
19399            <span class="suggest-header-text">Produk yang ditemukan (${items.length})</span>
19400          </div>
19401          <div class="suggest-header-hint">Tekan <kbd>Enter</kbd> untuk lihat semua hasil</div>
19402        </div>
19403      `;
19404      
19405      // Render product items
19406      html += items.map((it, idx)=>{
19407        // Normalisasi & fallback
19408        const id    = it.id != null ? it.id : '';
19409        const name  = esc(it.name);
19410        const slug  = esc(it.slug != null ? it.slug : it.id);
19411        const img   = esc(it.image || '/assets/img/no-image.png');
19412        const price = esc(it.price_label || '');
19413
19414       const url = '/product/' + encodeURIComponent(slug);
19415
19416        return `
19417          <div class="suggest-item" data-index="${idx}" data-url="${url}" data-id="${esc(id)}">
19418            <img src="${img}" alt="${name}" class="suggest-thumb">
19419            <div class="suggest-meta">
19420              <div class="suggest-name">${name}</div>
19421              <div class="suggest-price">${price}</div>
19422            </div>
19423          </div>
19424        `;
19425      }).join('');
19426      
19427      // Footer hint for more results - only show if 10 items (means there might be more)
19428      if (items.length >= 10) {
19429        html += `
19430          <div class="suggest-footer-hint">
19431            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="vertical-align: -3px; margin-right: 6px;">
19432              <path d="M9.29 6.71a.996.996 0 0 0 0 1.41L13.17 12l-3.88 3.88a.996.996 0 1 0 1.41 1.41l4.59-4.59a.996.996 0 0 0 0-1.41L10.7 6.7c-.38-.39-1.02-.39-1.41.01z"/>
19433            </svg>
19434            <span>Lihat hasil lengkap dengan menekan <kbd>Enter</kbd> atau <a href="#" class="suggest-footer-link" id="footerSearchLink">klik di sini</a></span>
19435          </div>
19436        `;
19437      }
19438      
19439      suggest.innerHTML = html;
19440      suggest.hidden = false;
19441      suggest.classList.add('open');
19442      suggest.style.display = 'block';
19443      suggest.style.visibility = 'visible';
19444      suggest.style.opacity = '1';
19445    }
19446
19447    function fetchRemote(q, shouldLog = false){
19448      // Cancel previous request if any
19449      if (currentAbortController) {
19450        currentAbortController.abort();
19451      }
19452      currentAbortController = new AbortController();
19453
19454      // Always use standard search for normal mode
19455      // Only add log=1 parameter when shouldLog is true (intentional search)
19456      const logParam = shouldLog ? '&log=1' : '';
19457      const url = '/api/search_products.php?q=' + encodeURIComponent(q) + logParam;
19458      const fetchOptions = {
19459        headers: {'Accept':'application/json'},
19460        signal: currentAbortController.signal
19461      };
19462
19463      return fetch(url, fetchOptions)
19464        .then(r=>r.json())
19465        .then(j=>{
19466          if (j.ok && Array.isArray(j.data)) {
19467            return j;
19468          }
19469          return { ok: false, data: [] };
19470        })
19471        .catch(err=>{
19472          if (err.name === 'AbortError') {
19473            return { ok: false, data: [] };
19474          }
19475          return { ok:false, data:[] };
19476        });
19477    }
19478
19479    function showNoResults(q, aiInsights) {
19480      suggest.innerHTML = `
19481        <div style="padding: 32px 24px; text-align: center; color: #64748b;">
19482          <div style="font-size: 56px; margin-bottom: 16px; opacity: 0.6;">🔍</div>
19483          <div style="font-size: 16px; font-weight: 600; color: #334155; margin-bottom: 8px;">
19484            Tidak ada hasil untuk "${esc(q)}"
19485          </div>
19486          ${aiInsights && aiInsights.explanation ? `
19487            <div style="font-size: 13px; margin: 16px auto; padding: 12px 16px; background: #f1f5f9; border-radius: 8px; max-width: 400px; color: #475569;">
19488              <strong style="color: #667eea;">🤖 AI memahami:</strong><br>
19489              ${esc(aiInsights.explanation)}
19490            </div>
19491          ` : ''}
19492          <div style="font-size: 13px; margin-top: 16px; opacity: 0.8;">
19493            Coba kata kunci lain atau hubungi kami untuk bantuan
19494          </div>
19495        </div>
19496      `;
19497      suggest.hidden = false;
19498      suggest.classList.add('open');
19499      suggest.style.display = 'block';
19500    }
19501
19502    function showErrorMessage() {
19503      suggest.innerHTML = `
19504        <div style="padding: 32px 24px; text-align: center; color: #dc2626;">
19505          <div style="font-size: 48px; margin-bottom: 12px;">⚠️</div>
19506          <div style="font-size: 14px; font-weight: 600; margin-bottom: 8px;">
19507            Pencarian tidak tersedia
19508          </div>
19509          <div style="font-size: 12px; opacity: 0.8;">
19510            Silakan coba lagi dalam beberapa saat
19511          </div>
19512        </div>
19513      `;
19514      suggest.hidden = false;
19515      suggest.classList.add('open');
19516      suggest.style.display = 'block';
19517    }
19518
19519    function showAIErrorState(q) {
19520      suggest.innerHTML = `
19521        <div class="ai-progress-container">
19522          <div class="ai-progress-header">
19523            <div class="ai-progress-icon" style="background: linear-gradient(135deg, #f59e0b 0%, #f97316 100%);">⚡</div>
19524            <div class="ai-progress-text">
19525              <div class="ai-progress-title">AI sedang memproses...</div>
19526              <div class="ai-progress-subtitle">Menganalisis query: "${esc(q)}"</div>
19527            </div>
19528          </div>
19529          <div style="padding: 16px; margin-top: 12px; background: #fef3c7; border-radius: 8px; border-left: 3px solid #f59e0b;">
19530            <div style="font-size: 13px; color: #92400e; line-height: 1.6;">
19531              <strong style="display: block; margin-bottom: 4px;">💡 Sedang mencari produk terbaik untuk Anda</strong>
19532              AI kami sedang menganalisis database untuk menemukan hasil yang paling relevan.
19533              Mohon tunggu sebentar...
19534            </div>
19535          </div>
19536          <div style="padding: 12px 16px; margin-top: 12px; text-align: center;">
19537            <button onclick="location.reload()" style="padding: 8px 16px; background: #fb923c; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600;">
19538              🔄 Refresh & Coba Lagi
19539            </button>
19540          </div>
19541        </div>
19542      `;
19543      suggest.hidden = false;
19544      suggest.classList.add('open');
19545      suggest.style.display = 'block';
19546    }
19547
19548    function triggerAISearch(q) {
19549      lastQuery = q; // Update lastQuery to prevent re-triggering
19550      isAIProcessing = true; // Set flag to prevent suggest box from closing
19551      
19552      // Show AI progress UI - keep suggest box visible
19553      suggest.innerHTML = showAIProgressSteps();
19554      suggest.hidden = false;
19555      suggest.classList.add('open');
19556      suggest.style.display = 'block';
19557      
19558      setTimeout(() => updateAIProgressStep(1), 1000);
19559      setTimeout(() => updateAIProgressStep(2), 2500);
19560
19561      // Cancel previous request if any
19562      if (currentAbortController) {
19563        currentAbortController.abort();
19564      }
19565      currentAbortController = new AbortController();
19566
19567      const url = '/api/search_ai.php?q=' + encodeURIComponent(q);
19568      const fetchOptions = {
19569        headers: {'Accept':'application/json'},
19570        signal: currentAbortController.signal
19571      };
19572      
19573      // Add timeout
19574      const timeoutId = setTimeout(() => {
19575        currentAbortController.abort();
19576        
19577        // Show fallback loading state
19578        suggest.innerHTML = `
19579          <div class="ai-progress-container">
19580            <div class="ai-progress-header">
19581              <div class="ai-progress-icon">⏳</div>
19582              <div class="ai-progress-text">
19583                <div class="ai-progress-title">AI timeout - switching to standard search</div>
19584                <div class="ai-progress-subtitle">Mencari dengan metode tradisional...</div>
19585              </div>
19586            </div>
19587          </div>
19588        `;
19589        
19590        // Fallback to traditional search
19591        fetch('/api/search_products.php?q=' + encodeURIComponent(q), {
19592          headers: { 'Accept': 'application/json' }
19593        })
19594          .then(r => r.json())
19595          .then(j => {
19596            isAIProcessing = false; // Reset flag
19597            if (j.ok && Array.isArray(j.data) && j.data.length) {
19598              renderSuggest(j.data, null);
19599            } else {
19600              showNoResults(q, null);
19601            }
19602          })
19603          .catch(() => {
19604            isAIProcessing = false; // Reset flag
19605            showAIErrorState(q);
19606          });
19607      }, AI_TIMEOUT);
19608
19609      fetch(url, fetchOptions)
19610        .then(r => r.json())
19611        .then(j => {
19612          clearTimeout(timeoutId);
19613          isAIProcessing = false; // Reset flag when done
19614          
19615          if (j.ok && Array.isArray(j.data)) {
19616            if (j.data.length > 0) {
19617              renderSuggest(j.data, j.ai_insights || null);
19618            } else {
19619              showNoResults(q, j.ai_insights || null);
19620            }
19621          } else {
19622            showAIErrorState(q);
19623          }
19624        })
19625        .catch(err => {
19626          clearTimeout(timeoutId);
19627          isAIProcessing = false; // Reset flag on error
19628          if (err.name !== 'AbortError') {
19629            showAIErrorState(q);
19630          }
19631        });
19632    }
19633
19634    function performSearch(q){
19635      if (q.length < MIN_LEN){
19636        clearSuggest();
19637        return;
19638      }
19639      if (q === lastQuery) return;
19640      lastQuery = q;
19641      
19642      // Normal mode only - AI mode uses triggerAISearch()
19643      fetchRemote(q).then(j=>{
19644        if (j.ok && Array.isArray(j.data)){
19645          if (j.data.length > 0) {
19646            renderSuggest(j.data, null);
19647          } else {
19648            showNoResults(q, null);
19649          }
19650        } else {
19651          clearSuggest();
19652        }
19653      }).catch(() => {
19654        clearSuggest();
19655      });
19656    }
19657
19658    function debounce(){
19659      const q = searchInput.value.trim();
19660      clearTimeout(debounceTimer);
19661      
19662      // If empty, show trending suggestions and products
19663      if (q.length === 0) {
19664        debounceTimer = setTimeout(async () => {
19665          const trends = trendingCache || await fetchTrending();
19666          const products = trendingProductsCache || await fetchTrendingProducts();
19667          if ((trends && trends.length > 0) || (products && products.length > 0)) {
19668            renderTrendingSuggestions(trends, products);
19669          } else {
19670            clearSuggest();
19671          }
19672        }, 100);
19673        return;
19674      }
19675      
19676      // For AI mode, just show search prompt (no auto search)
19677      if (isAIModeEnabled && q.length >= MIN_LEN) {
19678        showAISearchPrompt(q);
19679      } else if (!isAIModeEnabled && q.length >= MIN_LEN) {
19680        // Normal mode: auto search with debounce
19681        debounceTimer = setTimeout(()=> performSearch(q), DEBOUNCE);
19682      } else {
19683        clearSuggest();
19684      }
19685    }
19686
19687    function showAISearchPrompt(q) {
19688      suggest.innerHTML = `
19689        <div class="ai-search-prompt">
19690          <div class="ai-prompt-header">
19691            <div class="ai-prompt-icon">🤖</div>
19692            <div class="ai-prompt-text">
19693              <div class="ai-prompt-title">Pencarian AI Siap!</div>
19694              <div class="ai-prompt-subtitle">Klik tombol di bawah untuk mencari dengan AI</div>
19695            </div>
19696          </div>
19697          <div class="ai-prompt-query">
19698            <div class="ai-query-label">
19698Query Anda:</div>
19699            <div class="ai-query-text">"${esc(q)}"</div>
19700          </div>
19701          <button class="ai-search-trigger-btn" data-query="${esc(q)}">
19702            <span class="ai-btn-icon">✨</span>
19703            <span class="ai-btn-text">Cari dengan AI</span>
19704          </button>
19705          <div class="ai-prompt-footer">
19706            AI akan menganalisis query Anda untuk hasil yang lebih relevan
19707          </div>
19708        </div>
19709      `;
19710      suggest.hidden = false;
19711      suggest.classList.add('open');
19712      suggest.style.display = 'block';
19713    }
19714
19715    function moveHighlight(dir){
19716      if (suggest.hidden) return;
19717      const items = suggest.querySelectorAll('.suggest-item');
19718      if (!items.length) return;
19719      currentIndex += dir;
19720      if (currentIndex < 0) currentIndex = items.length - 1;
19721      if (currentIndex >= items.length) currentIndex = 0;
19722      items.forEach(it=> it.classList.remove('active'));
19723      const active = items[currentIndex];
19724      if (active){
19725        active.classList.add('active');
19726        active.scrollIntoView({block:'nearest'});
19727      }
19728    }
19729
19730    function activate(){
19731      const active = suggest.querySelector('.suggest-item.active');
19732      if (!active) return;
19733      window.location.href = active.getAttribute('data-url');
19734    }
19735
19736    searchInput.addEventListener('input', debounce);
19737    
19738    // Stop typing animation when focused & load trending if empty
19739    searchInput.addEventListener('focus', async () => {
19740      stopTypingAnimation();
19741      
19742      const q = searchInput.value.trim();
19743      
19744      // If empty, show trending suggestions and products
19745      if (q.length === 0) {
19746        const trends = trendingCache || await fetchTrending();
19747        const products = trendingProductsCache || await fetchTrendingProducts();
19748        if ((trends && trends.length > 0) || (products && products.length > 0)) {
19749          renderTrendingSuggestions(trends, products);
19750        }
19751        return;
19752      }
19753      
19754      // If AI mode and has query, show AI prompt
19755      if (isAIModeEnabled && q.length >= MIN_LEN && suggest.hidden) {
19756        showAISearchPrompt(q);
19757      }
19758    });
19759    
19760    // Resume typing animation when blurred (if suggest is closed)
19761    searchInput.addEventListener('blur', () => {
19762      // Add delay to allow click events to fire first
19763      setTimeout(() => {
19764        if (!isAIModeEnabled && suggest.hidden && searchInput.value.trim() === '') {
19765          startTypingAnimation();
19766        }
19767      }, 200);
19768    });
19769    
19770    searchInput.addEventListener('keydown', e=>{
19771      switch(e.key){
19772        case 'ArrowDown': moveHighlight(1); e.preventDefault(); break;
19773        case 'ArrowUp': moveHighlight(-1); e.preventDefault(); break;
19774        case 'Enter':
19775          if (!suggest.hidden){
19776            activate();
19777            e.preventDefault();
19778          } else {
19779            // Direct enter without suggestion - track search submit and log final search
19780            const q = searchInput.value.trim();
19781            if (q.length >= MIN_LEN) {
19782              trackSearchSubmit(q);
19783              logFinalSearch(q);
19784            }
19785          }
19786          break;
19787        case 'Escape': clearSuggest(); break;
19788      }
19789    });
19790
19791    suggest.addEventListener('click', e=>{
19792      // Check if footer link clicked
19793      const footerLink = e.target.closest('#footerSearchLink');
19794      if (footerLink) {
19795        e.preventDefault();
19796        const q = searchInput.value.trim();
19797        if (q.length >= MIN_LEN) {
19798          trackSearchSubmit(q);
19799          logFinalSearch(q);
19800          window.location.href = '/product/search/?q=' + encodeURIComponent(q);
19801        }
19802        return;
19803      }
19804      
19805      // Check if AI search button clicked
19806      const aiBtn = e.target.closest('.ai-search-trigger-btn');
19807      if (aiBtn) {
19808        const query = aiBtn.getAttribute('data-query');
19809        if (query) {
19810          triggerAISearch(query);
19811        }
19812        return;
19813      }
19814      
19815      // Handle trending item click
19816      const trendingItem = e.target.closest('.trending-item');
19817      if (trendingItem) {
19818        const query = trendingItem.getAttribute('data-query');
19819        if (query) {
19820          searchInput.value = query;
19821          trackTrendingClick(query);
19822          logFinalSearch(query); // Log final search when user clicks trending
19823          performSearch(query);
19824        }
19825        return;
19826      }
19827      
19828      // Handle trending product click
19829      const trendingProduct = e.target.closest('.trending-product-item');
19830      if (trendingProduct) {
19831        const productId = trendingProduct.getAttribute('data-product-id');
19832        const slug = trendingProduct.getAttribute('data-slug');
19833        const query = trendingProduct.getAttribute('data-query');
19834        
19835        if (productId && slug) {
19836          trackTrendingProductClick(productId, query);
19837          window.location.href = '/product/' + encodeURIComponent(slug);
19838        }
19839        return;
19840      }
19841      
19842      // Handle product item click
19843      const it = e.target.closest('.suggest-item');
19844      if (!it) return;
19845      
19846      const productId = it.getAttribute('data-id');
19847      trackSearchClick(lastQuery, productId);
19848      logFinalSearch(lastQuery); // Log final search when user clicks on suggestion
19849      
19850      window.location.href = it.getAttribute('data-url');
19851    });
19852
19853    document.addEventListener('click', e=>{
19854      // Don't close suggest box if AI is processing
19855      if (isAIProcessing) return;
19856      
19857      if (e.target === searchInput || e.target === aiToggleBtn || 
19858          suggest.contains(e.target) || aiToggleBtn.contains(e.target)) return;
19859      clearSuggest();
19860    });
19861
19862    if (searchBtn){
19863      searchBtn.addEventListener('click', ()=>{
19864        const q = searchInput.value.trim();
19865        if (q.length >= MIN_LEN){
19866          // Track search submit and log final search
19867          trackSearchSubmit(q);
19868          logFinalSearch(q);
19869          window.location.href = '/product/search/?q=' + encodeURIComponent(q);
19870        }
19871      });
19872    }
19873
19874    // Pastikan style dasar muncul
19875    if (!suggest.classList.contains('wired')) {
19876      suggest.classList.add('wired');
19877      suggest.style.position = 'absolute';
19878      suggest.style.zIndex = '9999';
19879      suggest.style.background = '#fff';
19880      suggest.style.border = '1px solid #ccc';
19881      suggest.style.boxShadow = '0 4px 12px rgba(0,0,0,0.12)';
19882      suggest.style.maxHeight = '360px';
19883      suggest.style.overflowY = 'auto';
19884    }
19885
19886    return true;
19887  }
19888
19889  if (!setup()) {
19890    if (document.readyState === 'loading') {
19891      document.addEventListener('DOMContentLoaded', setup);
19892    } else {
19893      setTimeout(setup, 80);
19894    }
19895  }
19896})();
19897</script>
vendor: 1 bytes, line 19897
19897
19898<script data-inlined="site-notice.js">
19899/**
19900 * Site Notice Banner - Interactive Behavior
19901 * Handles dismiss functionality with localStorage persistence
19902 * Supports dynamic notice IDs for different campaigns
19903 */
19904
19905(function() {
19906    'use strict';
19907
19908    const STORAGE_KEY = 'plazait_site_notice_dismissed';
19909    
19910    const banner = document.getElementById('holidayNotice');
19911    const closeBtn = document.getElementById('holidayNoticeClose');
19912    
19913    if (!banner || !closeBtn) return;
19914
19915    // Get notice ID from data attribute
19916    const noticeId = banner.dataset.noticeId;
19917    if (!noticeId) {
19918        console.warn('Notice ID not found. Banner will not persist dismiss state.');
19919    }
19920
19921    /**
19922     * Check if banner was previously dismissed
19923     */
19924    function wasDismissed() {
19925        if (!noticeId) return false;
19926        
19927        try {
19928            const dismissed = localStorage.getItem(STORAGE_KEY);
19929            return dismissed === noticeId;
19930        } catch (e) {
19931            console.warn('localStorage not available:', e);
19932            return false;
19933        }
19934    }
19935
19936    /**
19937     * Mark banner as dismissed
19938     */
19939    function markDismissed() {
19940        if (!noticeId) return;
19941        
19942        try {
19943            localStorage.setItem(STORAGE_KEY, noticeId);
19944        } catch (e) {
19945            console.warn('Could not save to localStorage:', e);
19946        }
19947    }
19948
19949    /**
19950     * Hide banner with animation
19951     */
19952    function hideBanner() {
19953        banner.classList.add('hidden');
19954        document.body.classList.remove('has-holiday-notice');
19955        
19956        // Remove from DOM after animation
19957        setTimeout(() => {
19958            banner.remove();
19959        }, 300);
19960        
19961        markDismissed();
19962        
19963        // Analytics tracking (optional)
19964        if (window.gtag) {
19965            window.gtag('event', 'banner_dismiss', {
19966                'event_category': 'Site Notice',
19967                'event_label': noticeId || 'unknown',
19968            });
19969        }
19970    }
19971
19972    /**
19973     * Initialize banner
19974     */
19975    function init() {
19976        // Check if already dismissed
19977        if (wasDismissed()) {
19978            banner.remove();
19979            return;
19980        }
19981        
19982        // Add body class for layout adjustment
19983        document.body.classList.add('has-holiday-notice');
19984        
19985        // Close button click
19986        closeBtn.addEventListener('click', hideBanner);
19987        
19988        // Keyboard support for close button
19989        closeBtn.addEventListener('keydown', (e) => {
19990            if (e.key === 'Enter' || e.key === ' ') {
19991                e.preventDefault();
19992                hideBanner();
19993            }
19994        });
19995        
19996        // Optional: Auto-hide after certain time (disabled by default)
19997        // setTimeout(hideBanner, 30000); // 30 seconds
19998    }
19999
20000    // Initialize when DOM is ready
20001    if (document.readyState === 'loading') {
20002        document.addEventListener('DOMContentLoaded', init);
20003    } else {
20004        init();
20005    }
20006
20007})();
20008
20009</script>
vendor: 1 bytes, line 20009
20009
20010<script data-inlined="guest-cart.js">
20011/**
20012 * Guest Cart Manager - localStorage based cart for guest users
20013 * 
20014 * Manages shopping cart items in browser localStorage for users who are not logged in.
20015 * Provides seamless transition to database when user logs in.
20016 */
20017
20018class GuestCart {
20019    constructor() {
20020        this.storageKey = 'plazait_guest_cart';
20021        this.apiBase = '/api/cart';
20022        
20023        // Initialize cart structure
20024        this.cart = this.loadFromStorage();
20025        
20026        // Bind methods
20027        this.add = this.add.bind(this);
20028        this.remove = this.remove.bind(this);
20029        this.update = this.update.bind(this);
20030        this.clear = this.clear.bind(this);
20031        this.getItems = this.getItems.bind(this);
20032        this.mergeToDatabase = this.mergeToDatabase.bind(this);
20033    }
20034    
20035    /**
20036     * Load cart from localStorage
20037     */
20038    loadFromStorage() {
20039        try {
20040            const stored = localStorage.getItem(this.storageKey);
20041            if (stored) {
20042                const parsed = JSON.parse(stored);
20043                if (parsed && typeof parsed === 'object' && parsed.items) {
20044                    return parsed;
20045                }
20046            }
20047        } catch (error) {
20048            // Silent error handling
20049        }
20050        
20051        return {
20052            items: {},
20053            updated_at: Date.now(),
20054            version: '1.0'
20055        };
20056    }
20057    
20058    /**
20059     * Save cart to localStorage
20060     */
20061    saveToStorage() {
20062        try {
20063            this.cart.updated_at = Date.now();
20064            localStorage.setItem(this.storageKey, JSON.stringify(this.cart));
20065            
20066            // Dispatch custom event for cart updates
20067            window.dispatchEvent(new CustomEvent('guestCartUpdated', {
20068                detail: {
20069                    itemsCount: this.getItemsCount(),
20070                    linesCount: this.getLinesCount(),
20071                    items: this.cart.items
20072                }
20073            }));
20074            
20075            return true;
20076        } catch (error) {
20077            return false;
20078        }
20079    }
20080    
20081    /**
20082     * Generate cart item key
20083     */
20084    generateKey(productId, variantId = 0) {
20085        return `p${productId}v${variantId}`;
20086    }
20087    
20088    /**
20089     * Add item to guest cart
20090     */
20091    add(productId, qty = 1, variantId = 0, meta = {}) {
20092        const key = this.generateKey(productId, variantId);
20093        
20094        // Validate inputs
20095        if (!productId || productId <= 0 || !qty || qty <= 0) {
20096            throw new Error('Invalid product ID or quantity');
20097        }
20098        
20099        qty = Math.max(1, Math.min(999, parseInt(qty)));
20100        
20101        // Initialize item if not exists
20102        if (!this.cart.items[key]) {
20103            this.cart.items[key] = {
20104                pid: parseInt(productId),
20105                vid: parseInt(variantId) || 0,
20106                qty: 0,
20107                name: meta.name || '',
20108                sku: meta.sku || '',
20109                price: parseInt(meta.price) || 0,
20110                image_url: meta.image_url || '',
20111                product_url: meta.product_url || '/product/' + parseInt(productId),
20112                added_at: Date.now()
20113            };
20114        }
20115        
20116        // Update quantity
20117        this.cart.items[key].qty = Math.max(1, Math.min(999, this.cart.items[key].qty + qty));
20118        
20119        // Update metadata if provided
20120        if (meta.name) this.cart.items[key].name = meta.name;
20121        if (meta.sku) this.cart.items[key].sku = meta.sku;
20122        if (meta.price) this.cart.items[key].price = parseInt(meta.price);
20123        if (meta.image_url) this.cart.items[key].image_url = meta.image_url;
20124        if (meta.product_url) this.cart.items[key].product_url = meta.product_url;
20125        
20126        this.saveToStorage();
20127        
20128        return {
20129            key,
20130            item: this.cart.items[key],
20131            itemsCount: this.getItemsCount(),
20132            linesCount: this.getLinesCount()
20133        };
20134    }
20135    
20136    /**
20137     * Remove item from guest cart
20138     */
20139    remove(productId, variantId = 0) {
20140        const key = this.generateKey(productId, variantId);
20141        
20142        if (this.cart.items[key]) {
20143            delete this.cart.items[key];
20144            this.saveToStorage();
20145            
20146            return true;
20147        }
20148        
20149        return false;
20150    }
20151    
20152    /**
20153     * Update item quantity
20154     */
20155    update(productId, qty, variantId = 0) {
20156        const key = this.generateKey(productId, variantId);
20157        
20158        if (!this.cart.items[key]) {
20159            return false;
20160        }
20161        
20162        qty = parseInt(qty);
20163        if (qty <= 0) {
20164            return this.remove(productId, variantId);
20165        }
20166        
20167        this.cart.items[key].qty = Math.max(1, Math.min(999, qty));
20168        this.saveToStorage();
20169        
20170        return true;
20171    }
20172    
20173    /**
20174     * Clear entire cart
20175     */
20176    clear() {
20177        this.cart = {
20178            items: {},
20179            updated_at: Date.now(),
20180            version: '1.0'
20181        };
20182        
20183        this.saveToStorage();
20184        
20185        return true;
20186    }
20187    
20188    /**
20189     * Get all cart items
20190     */
20191    getItems() {
20192        return this.cart.items;
20193    }
20194    
20195    /**
20196     * Get cart items as array
20197     */
20198    getItemsArray() {
20199        return Object.values(this.cart.items);
20200    }
20201    
20202    /**
20203     * Get total items count (sum of quantities)
20204     */
20205    getItemsCount() {
20206        return Object.values(this.cart.items).reduce((total, item) => total + (item.qty || 0), 0);
20207    }
20208    
20209    /**
20210     * Get lines count (number of different items)
20211     */
20212    getLinesCount() {
20213        return Object.keys(this.cart.items).length;
20214    }
20215    
20216    /**
20217     * Get cart summary
20218     */
20219    getSummary() {
20220        const itemsCount = this.getItemsCount();
20221        const linesCount = this.getLinesCount();
20222        const totalValue = Object.values(this.cart.items).reduce(
20223            (total, item) => total + ((item.qty || 0) * (item.price || 0)), 0
20224        );
20225        
20226        return {
20227            itemsCount,
20228            linesCount,
20229            totalValue,
20230            updatedAt: this.cart.updated_at
20231        };
20232    }
20233    
20234    /**
20235     * Check if cart has items
20236     */
20237    isEmpty() {
20238        return this.getLinesCount() === 0;
20239    }
20240    
20241    /**
20242     * Merge guest cart to database after user login
20243     */
20244    async mergeToDatabase() {
20245        const items = this.cart.items;
20246        
20247        if (Object.keys(items).length === 0) {
20248            return { ok: true, merged_count: 0, message: 'No items to merge' };
20249        }
20250        
20251        try {
20252            // Use new endpoint that handles localStorage format
20253            const response = await fetch(`${this.apiBase}/merge-localStorage.php`, {
20254                method: 'POST',
20255                headers: {
20256                    'Content-Type': 'application/json',
20257                },
20258                credentials: 'same-origin',
20259                body: JSON.stringify({
20260                    items: items  // Send localStorage format directly
20261                })
20262            });
20263            
20264            const result = await response.json();
20265            
20266            if (result.ok) {
20267                // Clear localStorage cart after successful merge
20268                this.clear();
20269                
20270                // Update cart counter
20271                if (window.updateCartCounter) {
20272                    try {
20273                        await window.updateCartCounter();
20274                    } catch (e) {
20275                        // Silent error handling
20276                    }
20277                }
20278                
20279                // Dispatch merge completion event
20280                window.dispatchEvent(new CustomEvent('guestCartMerged', {
20281                    detail: result
20282                }));
20283                
20284                return result;
20285            } else {
20286                throw new Error(result.error || 'Failed to merge cart');
20287            }
20288            
20289        } catch (error) {
20290            throw error;
20291        }
20292    }
20293    
20294    /**
20295     * Export cart data (for debugging or backup)
20296     */
20297    export() {
20298        return {
20299            ...this.cart,
20300            summary: this.getSummary()
20301        };
20302    }
20303    
20304    /**
20305     * Import cart data (for restore or testing)
20306     */
20307    import(cartData) {
20308        if (cartData && typeof cartData === 'object' && cartData.items) {
20309            this.cart = {
20310                items: cartData.items,
20311                updated_at: cartData.updated_at || Date.now(),
20312                version: cartData.version || '1.0'
20313            };
20314            
20315            this.saveToStorage();
20316            return true;
20317        }
20318        
20319        return false;
20320    }
20321}
20322
20323// Create global instance
20324window.guestCart = new GuestCart();
20325
20326// Auto-merge on page load if user is logged in
20327document.addEventListener('DOMContentLoaded', async function() {
20328    // Multiple ways to check if user is logged in
20329    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
20330                      document.body.classList.contains('logged-in') ||
20331                      window.userLoggedIn === true ||
20332                      document.body.dataset.userLoggedIn === 'true';
20333    
20334    if (isLoggedIn && !window.guestCart.isEmpty()) {
20335        // Small delay to ensure page is fully loaded
20336        setTimeout(async () => {
20337            try {
20338                const result = await window.guestCart.mergeToDatabase();
20339            } catch (error) {
20340                // Silent error handling
20341            }
20342        }, 500);
20343    }
20344});
20345
20346// Also listen for login events (if login happens via AJAX)
20347window.addEventListener('userLoggedIn', async function() {
20348    if (!window.guestCart.isEmpty()) {
20349        try {
20350            await window.guestCart.mergeToDatabase();
20351        } catch (error) {
20352            // Silent error handling
20353        }
20354    }
20355});
20356</script>
vendor: 1 bytes, line 20356
20356
20357<script data-inlined="cart.js">
20358(function(){
20359  function $(s,ctx=document){ return ctx.querySelector(s); }
20360  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
20361  
20362  // CSRF token helper
20363  function getCsrfToken() {
20364    const metaTag = document.querySelector('meta[name="csrf-token"]');
20365    return metaTag ? metaTag.getAttribute('content') : '';
20366  }
20367  
20368  function guessPdMeta(){
20369    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : ($('#pd-title')?.textContent || '');
20370    const sku  = (function(){ const el=$('#pd-sku'); return el ? el.textContent.trim() : (window.PD_SKU||''); })();
20371    // SECURITY FIX: Remove price from client-side metadata (will be validated server-side)
20372    const imgEl = $('#pd-main-img');
20373    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
20374    return { name, sku, image_url }; // price removed for security
20375  }
20376
20377  async function addToCart(productId, qty=1, variantId=0, metaOpt){
20378    const meta = Object.assign(guessPdMeta(), metaOpt||{});
20379    const payload = {
20380      product_id: parseInt(productId||0,10) || 0,
20381      qty: parseInt(qty||1,10) || 1,
20382      variant_id: parseInt(variantId||0,10) || 0,
20383      meta
20384    };
20385    
20386    // Check if user is logged in
20387    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
20388                      document.body.classList.contains('logged-in') ||
20389                      window.userLoggedIn === true ||
20390                      document.body.dataset.userLoggedIn === 'true';
20391    
20392    // Skip localStorage untuk logged user - mereka pakai database
20393    if (!isLoggedIn && window.guestCart) {
20394      try {
20395        window.guestCart.add(payload.product_id, payload.qty, payload.variant_id, meta);
20396      } catch (e) {
20397        console.warn('Failed to add to localStorage cart:', e);
20398      }
20399    }
20400    
20401    const res = await fetch('/api/cart/add.php', {
20402      method: 'POST',
20403      headers: { 
20404        'Content-Type':'application/json',
20405        'X-CSRF-Token': getCsrfToken()
20406      },
20407      credentials: 'same-origin',
20408      body: JSON.stringify({
20409        ...payload,
20410        csrf_token: getCsrfToken()
20411      }),
20412    });
20413    const data = await res.json().catch(()=>({ ok:false, error:'Gagal menambahkan ke keranjang' }));
20414    if (!res.ok || !data.ok) {
20415      const msg = data && data.error ? data.error : 'Gagal menambahkan ke keranjang';
20416      throw new Error(msg);
20417    }
20418
20419    // Update badge di navbar dengan data dari response
20420    try {
20421      const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
20422      if (el && data.cart && typeof data.cart.items_count === 'number') {
20423        el.textContent = String(data.cart.items_count);
20424        el.classList.toggle('is-empty', data.cart.items_count <= 0);
20425      }
20426    } catch(_){}
20427
20428    // Siarkan event global (dipakai cart-badge.js dan halaman lain)
20429    try {
20430      document.dispatchEvent(new CustomEvent('cart:updated', { detail: data.cart || {} }));
20431    } catch(_){}
20432
20433    // Update CartBadge dengan data dari response (lebih reliable)
20434    try { 
20435      if (window.CartBadge && window.CartBadge.set) {
20436        window.CartBadge.set(data.cart.items_count||0);
20437      }
20438    } catch(_){}
20439
20440    // Tambahan: refresh cart counter setelah delay kecil untuk memastikan data tersinkronisasi
20441    setTimeout(() => {
20442      if (window.updateCartCounter) {
20443        window.updateCartCounter();
20444      }
20445    }, 100);
20446
20447    return data;
20448  }
20449
20450  // Function to update cart counter from server
20451  async function updateCartCounter(){
20452    try {
20453      const res = await fetch('/api/cart/get.php', { credentials:'same-origin' });
20454      const data = await res.json().catch(()=>({ok:false}));
20455      if (data.ok && typeof data.items_count === 'number') {
20456        const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
20457        if (el) {
20458          el.textContent = String(data.items_count);
20459          el.classList.toggle('is-empty', data.items_count <= 0);
20460        }
20461        // Dispatch event
20462        try {
20463          document.dispatchEvent(new CustomEvent('cart:updated', { detail: { items_count: data.items_count } }));
20464        } catch(_){}
20465      }
20466    } catch (err) {
20467      console.log('Could not update cart counter:', err);
20468    }
20469  }
20470
20471  // Function to manually merge guest cart (if needed)
20472  async function mergeGuestCart(){
20473    try {
20474      const res = await fetch('/api/cart/merge-guest.php', {
20475        method: 'POST',
20476        headers: { 
20477          'Content-Type': 'application/json',
20478          'X-CSRF-Token': getCsrfToken()
20479        },
20480        credentials: 'same-origin',
20481        body: JSON.stringify({ csrf_token: getCsrfToken() })
20482      });
20483      const data = await res.json().catch(()=>({ok:false}));
20484      
20485      if (data.ok && data.merged_items > 0) {
20486        console.log(`Guest cart merged: ${data.merged_items} items`);
20487        // Update cart counter after merge
20488        await updateCartCounter();
20489        return data;
20490      }
20491      return data;
20492    } catch (err) {
20493      console.log('Could not merge guest cart:', err);
20494      return { ok: false, error: err.message };
20495    }
20496  }
20497
20498  window.addToCart = addToCart;
20499  window.updateCartCounter = updateCartCounter;
20500  window.mergeGuestCart = mergeGuestCart;
20501
20502  // Optional: auto-bind untuk tombol apapun yang punya data-addtocart
20503  document.addEventListener('click', async (e)=>{
20504    const btn = e.target.closest('[data-addtocart]');
20505    if (!btn) return;
20506    e.preventDefault();
20507    if (btn.disabled) return;
20508
20509    const pid = parseInt(btn.getAttribute('data-pid')||'0',10);
20510    const vid = parseInt(btn.getAttribute('data-vid')||'0',10);
20511    const qty = parseInt(btn.getAttribute('data-qty')||'1',10) || 1;
20512
20513    try {
20514      const data = await addToCart(pid, qty, vid);
20515      if (window.showToast) {
20516        const meta = guessPdMeta();
20517        window.showToast({
20518          type:'success',
20519          title:'Berhasil ditambahkan',
20520          message:`<strong>${esc(meta.name)}</strong>${meta.sku?` · <em>SKU: ${esc(meta.sku)}</em>`:''}<br>Qty: ${qty}`,
20521          duration: 3800,
20522          actions: [
20523            { label:'Lihat Keranjang', onClick: ()=> { window.location.href='/keranjang'; } },
20524            { label:'Lanjut Belanja', variant:'alt', onClick: ()=>{} }
20525          ]
20526        });
20527      }
20528    } catch (err) {
20529      if (window.showToast) {
20530        window.showToast({ type:'warn', title:'Gagal', message: String(err?.message||'Gagal menambahkan'), duration: 4200 });
20531      } else {
20532        console.error(err);
20533      }
20534    }
20535  });
20536})();
20537</script>
vendor: 1 bytes, line 20537
20537
20538<script data-inlined="mega-menu-tiles.js">
20539// Tile Mega Menu — Enhanced 2025 (Stagger animations, product counts, loading states)
20540// Includes safe-area hover handling (no backdrop flicker)
20541
20542(function(){
20543  const trigger   = document.querySelector('[data-mm2="trigger"]');
20544  const panel     = document.querySelector('[data-mm2="panel"]');
20545  const topView   = document.querySelector('[data-mm2="top"]');
20546  const detailView= document.querySelector('[data-mm2="detail"]');
20547  const backdrop  = document.querySelector('[data-mm2="backdrop"]');
20548  if (!trigger || !panel || !topView || !detailView) return;
20549
20550  const apiUrl = '/api/categories_tree.php?top=16&child=18&grand=14';
20551  const isDesktop = () => window.matchMedia('(min-width: 961px)').matches;
20552
20553  let data = [];
20554  let hoverTimer, closeTimer;
20555  let isLoading = false;
20556
20557  const esc = s => (s+'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
20558  
20559  // Mobile close button HTML
20560  const mobileCloseBtn = () => `
20561    <button type="button" class="mm2-mobile-close" data-mm2="close" aria-label="Tutup menu">
20562      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
20563        <line x1="18" y1="6" x2="6" y2="18"></line>
20564        <line x1="6" y1="6" x2="18" y2="18"></line>
20565      </svg>
20566    </button>
20567  `;
20568  
20569  // Category-specific gradient colors (16 unique combinations)
20570  const categoryGradients = [
20571    { from: '#e0e7ff', to: '#fff', border: '#c7d2fe', color: '#4f46e5' },  // Indigo
20572    { from: '#dbeafe', to: '#fff', border: '#bfdbfe', color: '#1e40af' },  // Blue
20573    { from: '#d1fae5', to: '#fff', border: '#a7f3d0', color: '#059669' },  // Green
20574    { from: '#fef3c7', to: '#fff', border: '#fde68a', color: '#d97706' },  // Amber
20575    { from: '#fce7f3', to: '#fff', border: '#fbcfe8', color: '#db2777' },  // Pink
20576    { from: '#e9d5ff', to: '#fff', border: '#d8b4fe', color: '#9333ea' },  // Purple
20577    { from: '#ffedd5', to: '#fff', border: '#fed7aa', color: '#ea580c' },  // Orange
20578    { from: '#ccfbf1', to: '#fff', border: '#99f6e4', color: '#0d9488' },  // Teal
20579    { from: '#fef9c3', to: '#fff', border: '#fef08a', color: '#ca8a04' },  // Yellow
20580    { from: '#ffe4e6', to: '#fff', border: '#fecdd3', color: '#e11d48' },  // Rose
20581    { from: '#e0f2fe', to: '#fff', border: '#bae6fd', color: '#0284c7' },  // Sky
20582    { from: '#f3e8ff', to: '#fff', border: '#e9d5ff', color: '#a855f7' },  // Violet
20583    { from: '#dcfce7', to: '#fff', border: '#bbf7d0', color: '#16a34a' },  // Emerald
20584    { from: '#fef2f2', to: '#fff', border: '#fecaca', color: '#dc2626' },  // Red
20585    { from: '#eff6ff', to: '#fff', border: '#dbeafe', color: '#2563eb' },  // Blue-600
20586    { from: '#f5f3ff', to: '#fff', border: '#ede9fe', color: '#7c3aed' },  // Violet-600
20587  ];
20588  
20589  const icon = () => `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7h5l2 3h11v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><path d="M3 7V5a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v3"></path></svg>`;
20590
20591  // Recent (optional future use)
20592  const REC_KEY='mm2_recent';
20593  const saveRecent=(path,label)=>{ try{
20594    let list=JSON.parse(localStorage.getItem(REC_KEY)||'[]').filter(x=>x&&x.path!==path);
20595    list.unshift({path,label,t:Date.now()}); localStorage.setItem(REC_KEY, JSON.stringify(list.slice(0,8)));
20596  }catch{} };
20597
20598  function isToSafeArea(e){
20599    const t = e && e.relatedTarget ? e.relatedTarget : null;
20600    if (!t) return false;
20601    return (trigger.contains(t) || panel.contains(t) || (backdrop && backdrop.contains(t)));
20602  }
20603
20604  function open(){ 
20605    document.body.classList.add('mm2-open'); 
20606    showTop(); 
20607    // Add entrance animation class
20608    setTimeout(() => panel.classList.add('mm2-animated'), 10);
20609  }
20610  function close(){ 
20611    document.body.classList.remove('mm2-open');
20612    panel.classList.remove('mm2-animated');
20613  }
20614  function showTop(){ 
20615    topView.classList.add('is-active'); 
20616    detailView.classList.remove('is-active'); 
20617  }
20618  
20619  // Loading skeleton
20620  function showLoading() {
20621    const skeletons = Array.from({length: 12}, (_, i) => `
20622      <div class="mm2-card mm2-skeleton" style="animation-delay: ${i * 0.02}s">
20623        <span class="mm2-ico skeleton-box"></span>
20624        <span style="flex: 1;">
20625          <div class="skeleton-line" style="width: 60%; height: 16px; margin-bottom: 6px;"></div>
20626          <div class="skeleton-line" style="width: 40%; height: 12px;"></div>
20627        </span>
20628      </div>
20629    `).join('');
20630    topView.innerHTML = `<div class="mm2-top"><div class="mm2-grid">${skeletons}</div></div>`;
20631  }
20632  
20633  // Count total products in a category tree
20634  function countProducts(node) {
20635    let count = node.product_count || 0;
20636    if (Array.isArray(node.children)) {
20637      node.children.forEach(child => {
20638        count += countProducts(child);
20639      });
20640    }
20641    return count;
20642  }
20643  
20644  // Cache for fetched thumbnails to avoid duplicate requests
20645  const thumbnailCache = new Map();
20646  
20647  // Fetch product thumbnails for a category
20648  function fetchCategoryThumbnails(categoryPath) {
20649    // Check cache first
20650    if (thumbnailCache.has(categoryPath)) {
20651      injectThumbnails(categoryPath, thumbnailCache.get(categoryPath));
20652      return;
20653    }
20654    
20655    const url = `/api/category_products_preview.php?path=${encodeURIComponent(categoryPath)}&limit=2`;
20656    
20657    fetch(url)
20658      .then(r => {
20659        if (!r.ok) throw new Error('Network error');
20660        return r.json();
20661      })
20662      .then(data => {
20663        if (data.success && data.products && data.products.length > 0) {
20664          thumbnailCache.set(categoryPath, data.products);
20665          injectThumbnails(categoryPath, data.products);
20666        } else {
20667          // No products, remove skeleton
20668          removeThumbSkeletons(categoryPath);
20669        }
20670      })
20671      .catch(err => {
20672        console.warn('Failed to load thumbnails for', categoryPath, err);
20673        removeThumbSkeletons(categoryPath);
20674      });
20675  }
20676  
20677  // Inject thumbnails into the DOM
20678  function injectThumbnails(categoryPath, products) {
20679    const links = detailView.querySelectorAll(`[data-cat-path="${categoryPath}"]`);
20680    links.forEach(link => {
20681      const thumbsContainer = link.querySelector('.mm2-thumbs');
20682      if (!thumbsContainer) return;
20683      
20684      const thumbsHtml = products.slice(0, 2).map(p => 
20685        `<img src="${esc(p.img)}" alt="${esc(p.name)}" class="mm2-thumb" title="${esc(p.name)}" loading="lazy">`
20686      ).join('');
20687      
20688      thumbsContainer.innerHTML = thumbsHtml;
20689      thumbsContainer.removeAttribute('data-loading');
20690    });
20691  }
20692  
20693  // Remove skeleton loaders when no products
20694  function removeThumbSkeletons(categoryPath) {
20695    const links = detailView.querySelectorAll(`[data-cat-path="${categoryPath}"]`);
20696    links.forEach(link => {
20697      const thumbsContainer = link.querySelector('.mm2-thumbs');
20698      if (thumbsContainer) {
20699        thumbsContainer.remove();
20700      }
20701    });
20702  }
20703
20704  function showDetail(node){
20705    const title = node.label.split('/')[0];
20706    const children = Array.isArray(node.children) ? node.children : [];
20707    const hasChildren = children.length > 0;
20708
20709    const subnav = hasChildren ? `
20710      <nav class="mm2-subnav" role="tablist">
20711        ${children.map((c2,i)=>{
20712          const t = c2.label.split('/').slice(-1)[0];
20713          return `<button class="mm2-subitem${i===0?' active':''}" role="tab" data-idx="${i}" aria-selected="${i===0?'true':'false'}">${esc(t)}</button>`;
20714        }).join('')}
20715      </nav>` : '';
20716
20717    const subcontent = `<div class="mm2-subcontent" data-mm2="subcontent"></div>`;
20718
20719    detailView.innerHTML = `
20720      <div class="mm2-detail">
20721        <div class="mm2-detail-header">
20722          <button class="mm2-back" type="button" data-mm2="back" aria-label="Kembali">
20723            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6"></polyline></svg>
20724          </button>
20725          <div class="mm2-detail-title">${esc(title)}</div>
20726        </div>
20727
20728        ${hasChildren ? `
20729          <div class="mm2-detail-layout">
20730            ${subnav}
20731            ${subcontent}
20732          </div>` : `
20733          <div class="mm2-links">
20734            <a class="mm2-link mm2-seeall" href="/product/kategori/?kategori_path=${encodeURIComponent(node.path)}" data-mm2="nav" data-path="${esc(node.path)}" data-label="${esc(node.label)}">
20735              <strong>Lihat semua ${esc(title)}</strong>
20736            </a>
20737          </div>
20738        `}
20739      </div>
20740    `;
20741
20742    topView.classList.remove('is-active');
20743    detailView.classList.add('is-active');
20744
20745    if (hasChildren) {
20746      const subEl  = detailView.querySelector('.mm2-subnav');
20747      const contEl = detailView.querySelector('[data-mm2="subcontent"]');
20748      let activeIdx = 0;
20749
20750      const renderSubgrid = (idx) => {
20751        const c2 = children[idx]; if (!c2) { contEl.innerHTML=''; return; }
20752        const title2 = c2.label.split('/').slice(-1)[0];
20753        const l3 = Array.isArray(c2.children) ? c2.children : [];
20754        const l3Html = l3.map(c3 => `
20755          <a class="mm2-link" href="/product/kategori/?kategori_path=${encodeURIComponent(c3.path)}" data-mm2="nav" data-path="${esc(c3.path)}" data-label="${esc(c3.label)}" data-cat-path="${esc(c3.path)}">
20756            <span class="mm2-link-text">${esc(c3.label.split('/').slice(-1)[0])}</span>
20757            <span class="mm2-thumbs" data-loading="true">
20758              <span class="mm2-thumb-skeleton"></span>
20759              <span class="mm2-thumb-skeleton"></span>
20760            </span>
20761          </a>`).join('');
20762
20763        contEl.innerHTML = `
20764          <div class="mm2-links">
20765            <a class="mm2-link mm2-seeall" href="/product/kategori/?kategori_path=${encodeURIComponent(c2.path)}" data-mm2="nav" data-path="${esc(c2.path)}" data-label="${esc(c2.label)}">
20766              <strong>Lihat semua ${esc(title2)}</strong>
20767            </a>
20768            ${l3Html}
20769          </div>
20770        `;
20771        
20772        // Fetch product thumbnails for each level-3 category (desktop only)
20773        if (isDesktop()) {
20774          setTimeout(() => {
20775            l3.forEach(c3 => {
20776              fetchCategoryThumbnails(c3.path);
20777            });
20778          }, 100);
20779        }
20780      };
20781
20782      renderSubgrid(activeIdx);
20783
20784      // Desktop: hover ganti konten; Mobile & Desktop: click pilih
20785      subEl.addEventListener('mousemove', (e)=>{
20786        if (!isDesktop()) return;
20787        const it = e.target.closest('.mm2-subitem'); if (!it) return;
20788        const idx = +it.dataset.idx; if (!Number.isInteger(idx) || idx===activeIdx) return;
20789        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.classList.remove('active'));
20790        it.classList.add('active');
20791        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.setAttribute('aria-selected', el===it ? 'true':'false'));
20792        activeIdx = idx;
20793        renderSubgrid(activeIdx);
20794      });
20795
20796      subEl.addEventListener('click', (e)=>{
20797        const it = e.target.closest('.mm2-subitem'); if (!it) return;
20798        const idx = +it.dataset.idx; if (!Number.isInteger(idx)) return;
20799        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.classList.remove('active'));
20800        it.classList.add('active');
20801        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.setAttribute('aria-selected', el===it ? 'true':'false'));
20802        activeIdx = idx;
20803        renderSubgrid(activeIdx);
20804      });
20805
20806      // Delegate link clicks to add recent
20807      detailView.addEventListener('click', (e)=>{
20808        const nav = e.target.closest('[data-mm2="nav"]');
20809        if (nav) { saveRecent(nav.dataset.path, nav.dataset.label); }
20810      });
20811    }
20812  }
20813
20814  function buildTop(){
20815    const html = data.map((n, idx) => {
20816      const title = n.label.split('/')[0];
20817      const childCount = (n.children||[]).length;
20818      const productCount = countProducts(n);
20819      const gradient = categoryGradients[idx % categoryGradients.length];
20820      
20821      // Dynamic subtitle based on content - shorter for mobile
20822      let sub = '';
20823      if (productCount > 0) {
20824        sub = `${productCount} produk`;
20825        if (childCount > 0) sub += ` · ${childCount} kat`;
20826      } else if (childCount > 0) {
20827        sub = `${childCount} kategori`;
20828      } else {
20829        sub = 'Lihat semua';
20830      }
20831      
20832      const iconStyle = `background: linear-gradient(135deg, ${gradient.from}, ${gradient.to}); border-color: ${gradient.border}; color: ${gradient.color};`;
20833      
20834      return `<a href="/product/kategori/?kategori_path=${encodeURIComponent(n.path)}" class="mm2-card" data-mm2="card" data-path="${esc(n.path)}" data-label="${esc(n.label)}">
20835        <span class="mm2-ico" style="${iconStyle}">${icon()}</span>
20836        <span style="flex: 1;">
20837          <div class="mm2-title">${esc(title)}</div>
20838          <div class="mm2-sub">${esc(sub)}</div>
20839        </span>
20840      </a>`;
20841    }).join('');
20842    const closeBtnHtml = !isDesktop() ? mobileCloseBtn() : '';
20843    topView.innerHTML = `<div class="mm2-top">${closeBtnHtml}<div class="mm2-grid">${html}</div></div>`;
20844  }
20845
20846  // Open/close with safe-area
20847  trigger.addEventListener('click', (e)=>{ e.preventDefault(); document.body.classList.contains('mm2-open') ? close() : open(); });
20848  trigger.addEventListener('pointerenter', ()=>{ if (isDesktop()) { clearTimeout(closeTimer); hoverTimer=setTimeout(open, 80); }});
20849  trigger.addEventListener('pointerleave', (e)=>{ if (isDesktop() && !isToSafeArea(e)) { clearTimeout(hoverTimer); closeTimer=setTimeout(close, 200); }});
20850  panel.addEventListener('pointerenter', ()=>{ if (isDesktop()) clearTimeout(closeTimer); });
20851  panel.addEventListener('pointerleave', (e)=>{ if (isDesktop() && !isToSafeArea(e)) closeTimer=setTimeout(close, 200); });
20852  backdrop?.addEventListener('pointerenter', ()=>{ clearTimeout(closeTimer); });
20853  backdrop?.addEventListener('click', close);
20854  document.addEventListener('keydown', (e)=>{ if (e.key==='Escape') close(); });
20855
20856  // Card click → open detail (unless cmd/ctrl to open link)
20857  panel.addEventListener('click', (e)=>{
20858    const closeBtn = e.target.closest('[data-mm2="close"]'); if (closeBtn) { e.preventDefault(); close(); return; }
20859    const back = e.target.closest('[data-mm2="back"]'); if (back) { e.preventDefault(); showTop(); return; }
20860    const card = e.target.closest('[data-mm2="card"]');
20861    if (card && !e.metaKey && !e.ctrlKey) {
20862      e.preventDefault();
20863      const n = data.find(d => d.path === card.dataset.path);
20864      if (n) showDetail(n);
20865    }
20866    const nav = e.target.closest('[data-mm2="nav"]');
20867    if (nav) { saveRecent(nav.dataset.path, nav.dataset.label); }
20868  });
20869
20870  // Swipe-down gesture for mobile (bottom sheet close)
20871  if (!isDesktop()) {
20872    let startY = 0, currentY = 0, isDragging = false, startTime = 0;
20873    const SWIPE_THRESHOLD = 80;
20874    const VELOCITY_THRESHOLD = 0.3;
20875
20876    panel.addEventListener('touchstart', (e) => {
20877      if (!document.body.classList.contains('mm2-open')) return;
20878      const touch = e.touches[0];
20879      const scrollableContent = e.target.closest('.mm2-top, .mm2-detail');
20880      
20881      // Prevent drag if content is scrolled
20882      if (scrollableContent && scrollableContent.scrollTop > 5) return;
20883      
20884      // Only start drag from top area or handle
20885      const rect = panel.getBoundingClientRect();
20886      if (touch.clientY - rect.top > 60) return;
20887      
20888      startY = touch.clientY;
20889      currentY = startY;
20890      startTime = Date.now();
20891      isDragging = true;
20892    }, { passive: true });
20893
20894    panel.addEventListener('touchmove', (e) => {
20895      if (!isDragging) return;
20896      const touch = e.touches[0];
20897      currentY = touch.clientY;
20898      const deltaY = currentY - startY;
20899      
20900      // Only allow downward swipe
20901      if (deltaY > 0) {
20902        const opacity = Math.max(0.4, 1 - (deltaY / 300));
20903        panel.style.transform = `translateX(0) translateY(${deltaY}px)`;
20904        panel.style.opacity = opacity;
20905      }
20906    }, { passive: true });
20907
20908    const endDrag = () => {
20909      if (!isDragging) return;
20910      const deltaY = currentY - startY;
20911      const deltaTime = Date.now() - startTime;
20912      const velocity = deltaY / deltaTime;
20913      
20914      // Close if swiped down enough or fast swipe
20915      if (deltaY > SWIPE_THRESHOLD || velocity > VELOCITY_THRESHOLD) {
20916        close();
20917      }
20918      
20919      // Reset position
20920      panel.style.transform = '';
20921      panel.style.opacity = '';
20922      isDragging = false;
20923    };
20924
20925    panel.addEventListener('touchend', endDrag, { passive: true });
20926    panel.addEventListener('touchcancel', endDrag, { passive: true });
20927  }
20928
20929  // Fetch data with loading state
20930  if (!isLoading) {
20931    isLoading = true;
20932    showLoading();
20933    
20934    fetch(apiUrl, { headers: { 'Accept': 'application/json' }})
20935      .then(r => {
20936        if (!r.ok) throw new Error('Network response was not ok');
20937        return r.json();
20938      })
20939      .then(j => { 
20940        data = Array.isArray(j.data) ? j.data : []; 
20941        if (!data.length) {
20942          topView.innerHTML = `<div class="mm2-top"><div style="text-align:center;padding:40px;color:var(--mm2-fg-muted);">
20943            <p>Tidak ada kategori tersedia</p>
20944          </div></div>`;
20945          return;
20946        }
20947        buildTop();
20948        isLoading = false;
20949      })
20950      .catch(err => {
20951        console.error('Failed to load categories:', err);
20952        topView.innerHTML = `<div class="mm2-top"><div style="text-align:center;padding:40px;color:var(--mm2-fg-muted);">
20953          <p>Gagal memuat kategori. Silakan coba lagi.</p>
20954        </div></div>`;
20955        isLoading = false;
20956      });
20957  }
20958})();
20959
20960// Add skeleton animation styles
20961const style = document.createElement('style');
20962style.textContent = `
20963.mm2-skeleton {
20964  pointer-events: none;
20965  animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
20966}
20967.skeleton-box, .skeleton-line {
20968  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
20969  background-size: 200% 100%;
20970  animation: shimmer 1.5s infinite;
20971  border-radius: 8px;
20972}
20973@keyframes pulse {
20974  0%, 100% { opacity: 1; }
20975  50% { opacity: 0.6; }
20976}
20977@keyframes shimmer {
20978  0% { background-position: 200% 0; }
20979  100% { background-position: -200% 0; }
20980}
20981`;
20982document.head.appendChild(style);
20983</script>
20983
20984    
20984<script data-inlined="benchmark.js">
20985/**
20986 * Benchmark Data Loader
20987 * Handles AJAX fetch and display of benchmark data
20988 */
20989
20990(function() {
20991    'use strict';
20992    
20993    // Wait for DOM ready
20994    if (document.readyState === 'loading') {
20995        document.addEventListener('DOMContentLoaded', initBenchmark);
20996    } else {
20997        initBenchmark();
20998    }
20999    
21000    function initBenchmark() {
21001        const section = document.querySelector('.pd-benchmark-section');
21002        if (!section) return;
21003        
21004        const button = document.getElementById('btn-load-benchmark');
21005        const content = document.getElementById('benchmark-content');
21006        const productId = section.dataset.productId;
21007        
21008        if (!button || !content || !productId) return;
21009        
21010        let isLoading = false;
21011        let dataLoaded = false;
21012        
21013        button.addEventListener('click', function() {
21014            if (isLoading || dataLoaded) return;
21015            
21016            loadBenchmarkData(productId, button, content);
21017        });
21018    }
21019    
21020    function loadBenchmarkData(productId, button, container) {
21021        // Update button state
21022        button.disabled = true;
21023        button.classList.add('loading');
21024        const originalText = button.querySelector('.btn-text').textContent;
21025        button.querySelector('.btn-text').textContent = 'Memuat data...';
21026        
21027        // Show loading state
21028        container.style.display = 'block';
21029        container.innerHTML = `
21030            <div class="benchmark-loading">
21031                <div class="benchmark-loading-spinner"></div>
21032                <p>Mengambil data benchmark...</p>
21033            </div>
21034        `;
21035        
21036        // Fetch data
21037        fetch('/api/benchmark/fetch.php?product_id=' + productId)
21038            .then(function(response) {
21039                if (!response.ok) {
21040                    throw new Error('HTTP error ' + response.status);
21041                }
21042                return response.json();
21043            })
21044            .then(function(result) {
21045                if (result.success && result.data) {
21046                    renderBenchmarkData(result.data, container);
21047                    button.style.display = 'none'; // Hide button after successful load
21048                } else {
21049                    throw new Error(result.error || 'Data tidak tersedia');
21050                }
21051            })
21052            .catch(function(error) {
21053                console.error('Benchmark fetch error:', error);
21054                container.innerHTML = `
21055                    <div class="benchmark-error">
21056                        <p><strong>⚠️ Data Benchmark Tidak Tersedia</strong></p>
21057                        <p style="font-size: 14px; margin-top: 8px;">
21058                            ${error.message || 'Terjadi kesalahan saat memuat data'}
21059                        </p>
21060                        <button type="button" onclick="location.reload()" 
21061                                style="margin-top: 16px; padding: 8px 20px; background: #667eea; color: #fff; border: none; border-radius: 6px; cursor: pointer;">
21062                            Coba Lagi
21063                        </button>
21064                    </div>
21065                `;
21066                
21067                // Reset button
21068                button.disabled = false;
21069                button.classList.remove('loading');
21070                button.querySelector('.btn-text').textContent = originalText;
21071            });
21072    }
21073    
21074    function renderBenchmarkData(data, container) {
21075        let html = '<div class="benchmark-data-wrapper">';
21076        
21077        // Chipset info if available
21078        if (data.chipset) {
21079            html += `
21080                <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 10px 16px; border-radius: 8px; margin-bottom: 12px;">
21081                    <div style="font-size: 11px; opacity: 0.85; margin-bottom: 2px;">Chipset</div>
21082                    <div style="font-size: 15px; font-weight: 600;">${escapeHtml(data.chipset)}</div>
21083                </div>
21084            `;
21085        }
21086        
21087        // Main benchmark scores
21088        if (data.antutu || data.geekbench_multi || data.overall_score) {
21089            html += '<div class="benchmark-data-grid">';
21090            
21091            if (data.antutu) {
21092                html += `
21093                    <div class="benchmark-card">
21094                        <div class="benchmark-card-label">ANTUTU</div>
21095                        <div class="benchmark-card-value large">${formatNumber(data.antutu)}</div>
21096                        ${getScoreBadge(data.antutu, 'antutu')}
21097                    </div>
21098                `;
21099            }
21100            
21101            if (data.geekbench_multi) {
21102                html += `
21103                    <div class="benchmark-card">
21104                        <div class="benchmark-card-label">GEEKBENCH MULTI</div>
21105                        <div class="benchmark-card-value">${formatNumber(data.geekbench_multi)}</div>
21106                        ${data.geekbench_single ? '<div class="benchmark-card-subtitle">Single: ' + formatNumber(data.geekbench_single) + '</div>' : ''}
21107                    </div>
21108                `;
21109            }
21110            
21111            if (data.gaming_score) {
21112                html += `
21113                    <div class="benchmark-card">
21114                        <div class="benchmark-card-label">GAMING</div>
21115                        <div class="benchmark-card-value">${data.gaming_score}<span style="font-size: 14px; opacity: 0.6;">/100</span></div>
21116                    </div>
21117                `;
21118            }
21119            
21120            if (data.battery_score) {
21121                html += `
21122                    <div class="benchmark-card">
21123                        <div class="benchmark-card-label">BATTERY</div>
21124                        <div class="benchmark-card-value">${data.battery_score}<span style="font-size: 14px; opacity: 0.6;">/100</span></div>
21125                    </div>
21126                `;
21127            }
21128            
21129            if (data.overall_score) {
21130                html += `
21131                    <div class="benchmark-card">
21132                        <div class="benchmark-card-label">OVERALL</div>
21133                        <div class="benchmark-card-value">${data.overall_score}<span style="font-size: 14px; opacity: 0.6;">/100</span></div>
21134                    </div>
21135                `;
21136            }
21137            
21138            html += '</div>';
21139        }
21140        
21141        // AI Performance Description
21142        if (data.notes) {
21143            html += `
21144                <div style="background: #f8f9fa; border-left: 3px solid #667eea; padding: 10px 14px; border-radius: 6px; margin-top: 12px;">
21145                    <div style="font-size: 11px; font-weight: 600; color: #667eea; margin-bottom: 6px;">💡 Analisis Performa</div>
21146                    <div style="font-size: 13px; color: #4a5568; line-height: 1.5;">${escapeHtml(data.notes)}</div>
21147                </div>
21148            `;
21149        }
21150        
21151        // Attribution
21152        html += `
21153            <div class="benchmark-attribution">
21154                <p style="font-size: 11px; margin: 8px 0 0 0; color: #888;">Data benchmark dianalisis menggunakan AI dari spesifikasi produk</p>
21155                ${data.fetched_at ? '<p style="font-size: 10px; margin: 4px 0 0 0; color: #aaa;">Terakhir diperbarui: ' + formatDate(data.fetched_at) + '</p>' : ''}
21156            </div>
21157        `;
21158        
21159        html += '</div>';
21160        
21161        container.innerHTML = html;
21162    }
21163    
21164    function formatNumber(num) {
21165        if (!num) return '-';
21166        return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
21167    }
21168    
21169    function escapeHtml(text) {
21170        if (!text) return '';
21171        const div = document.createElement('div');
21172        div.textContent = text;
21173        return div.innerHTML;
21174    }
21175    
21176    function formatDate(dateStr) {
21177        try {
21178            const date = new Date(dateStr);
21179            const options = { year: 'numeric', month: 'long', day: 'numeric' };
21180            return date.toLocaleDateString('id-ID', options);
21181        } catch (e) {
21182            return dateStr;
21183        }
21184    }
21185    
21186    function getScoreBadge(score, type) {
21187        let category = '';
21188        let label = '';
21189        
21190        if (type === 'antutu') {
21191            if (score >= 500000) {
21192                category = 'excellent';
21193                label = 'Flagship';
21194            } else if (score >= 350000) {
21195                category = 'good';
21196                label = 'High-End';
21197            } else if (score >= 200000) {
21198                category = 'average';
21199                label = 'Mid-Range';
21200            } else {
21201                category = 'average';
21202                label = 'Entry-Level';
21203            }
21204        }
21205        
21206        if (label) {
21207            return '<div class="benchmark-score-badge ' + category + '">' + label + '</div>';
21208        }
21209        
21210        return '';
21211    }
21212})();
21213
21214</script>
21214
21215  
21215<script data-inlined="product-compare.js">
21216(function() {
21217  'use strict';
21218  
21219  // State management
21220  let compareList = [];
21221  let isAnalyzing = false;
21222  let currentAnalysis = null;
21223  
21224  // DOM elements
21225  const compareBtn = document.getElementById('compareBtn');
21226  const compareSidebar = document.getElementById('compareSidebar');
21227  const compareOverlay = document.getElementById('compareOverlay');
21228  const compareClose = document.getElementById('compareClose');
21229  const compareModal = document.getElementById('aiCompareModal');
21230  const compareModalClose = document.getElementById('aiCompareModalClose');
21231  
21232  // Initialize
21233  document.addEventListener('DOMContentLoaded', async function() {
21234    await initializeCompare();
21235    setupEventListeners();
21236  });
21237  
21238  /**
21239   * Initialize compare functionality
21240   */
21241  async function initializeCompare() {
21242    // Load compare list first before setting up buttons
21243    await loadCompareList();
21244    
21245    // Setup product detail compare button if exists
21246    const productCompareBtn = document.getElementById('pd-compare-btn');
21247    
21248    if (productCompareBtn) {
21249      setupProductCompareButton(productCompareBtn);
21250    }
21251    
21252    // Check for existing compare list and update UI
21253    if (compareList.length > 0) {
21254      // Only update badge and buttons, no sticky bar
21255    }
21256  }
21257  
21258  /**
21259   * Setup event listeners
21260   */
21261  function setupEventListeners() {
21262    // Navigation compare button
21263    if (compareBtn) {
21264      compareBtn.addEventListener('click', toggleCompareSidebar);
21265    }
21266    
21267    // Sidebar controls
21268    if (compareClose) {
21269      compareClose.addEventListener('click', closeCompareSidebar);
21270    }
21271    
21272    if (compareOverlay) {
21273      compareOverlay.addEventListener('click', closeCompareSidebar);
21274    }
21275    
21276    // Sidebar actions
21277    const clearAllBtn = document.getElementById('compareClearAll');
21278    if (clearAllBtn) {
21279      clearAllBtn.addEventListener('click', clearAllProducts);
21280    }
21281    
21282    const analyzeBtn = document.getElementById('compareAnalyze');
21283    if (analyzeBtn) {
21284      analyzeBtn.addEventListener('click', startAIAnalysis);
21285    }
21286    
21287    // Modal controls
21288    if (compareModalClose) {
21289      compareModalClose.addEventListener('click', closeModal);
21290    }
21291    
21292    const modalBackdrop = compareModal?.querySelector('.modal-backdrop');
21293    if (modalBackdrop) {
21294      modalBackdrop.addEventListener('click', closeModal);
21295    }
21296    
21297    // Modal actions
21298    const retryBtn = document.getElementById('aiRetry');
21299    if (retryBtn) {
21300      retryBtn.addEventListener('click', startAIAnalysis);
21301    }
21302    
21303    // Removed share and add to cart buttons - replaced with AI disclaimer
21304  }
21305  
21306  /**
21307   * Setup product detail compare button
21308   */
21309  function setupProductCompareButton(button) {
21310    const productId = parseInt(button.dataset.productId);
21311    if (!productId) return;
21312    
21313    // Check if product is already in compare list
21314    updateProductCompareButtonState(button, productId);
21315    
21316    button.addEventListener('click', async function() {
21317      // Get current variant ID (if any)
21318      const variantIdElement = document.getElementById('pd-variant-id');
21319      const variantId = variantIdElement ? parseInt(variantIdElement.value) || 0 : 0;
21320      
21321      // Update button's variant data for current selection
21322      button.dataset.variantId = variantId.toString();
21323      
21324      await toggleProductInCompare(productId, button, variantId);
21325    });
21326  }
21327  
21328  /**
21329   * Toggle product in compare list
21330   */
21331  async function toggleProductInCompare(productId, button, variantId = 0) {
21332    // Check by product ID (variants of same product treated as same item)
21333    const isInCompare = compareList.some(p => parseInt(p.id) === parseInt(productId));
21334    
21335    try {
21336      if (isInCompare) {
21337        await removeFromCompare(productId);
21338        showToast('Produk dihapus dari perbandingan', 'success');
21339      } else {
21340        await addToCompare(productId, variantId);
21341        showToast('Produk ditambahkan ke perbandingan', 'success');
21342        
21343        // Trigger fly-to-compare animation
21344        triggerFlyToCompareAnimation(button);
21345      }
21346      
21347      updateProductCompareButtonState(button, productId);
21348      updateCompareBadge();
21349      updateAllCompareButtons();
21350      
21351    } catch (error) {
21352      showToast(error.message || 'Terjadi kesalahan', 'error');
21353    }
21354  }
21355  
21356  /**
21357   * Update product compare button state
21358   */
21359  function updateProductCompareButtonState(button, productId) {
21360    // Ensure both IDs are compared as integers
21361    const isInCompare = compareList.some(p => parseInt(p.id) === parseInt(productId));
21362    button.classList.toggle('added', isInCompare);
21363    
21364    // Handle visibility of text elements
21365    const compareText = button.querySelector('.compare-text');
21366    const compareRemove = button.querySelector('.compare-remove');
21367    const compareNote = document.getElementById('compare-note');
21368    
21369    if (compareText) {
21370      compareText.style.display = isInCompare ? 'none' : 'inline';
21371    }
21372    
21373    if (compareRemove) {
21374      if (isInCompare) {
21375        compareRemove.removeAttribute('hidden');
21376        compareRemove.style.display = 'flex';
21377      } else {
21378        compareRemove.setAttribute('hidden', '');
21379        compareRemove.style.display = 'none';
21380      }
21381    }
21382    
21383    // Update info note
21384    if (compareNote) {
21385      if (isInCompare) {
21386        const currentCount = compareList.length;
21387        compareNote.textContent = `Produk sudah ada di daftar perbandingan (${currentCount}/5). Klik tombol untuk menghapus.`;
21388      } else {
21389        compareNote.textContent = 'Bandingkan hingga 5 produk dengan bantuan AI';
21390      }
21391    }
21392    
21393    // Inline compare preview removed - keeping it simple
21394  }
21395  
21396  /**
21397   * Add product to compare list
21398   */
21399  async function addToCompare(productId, variantId = 0) {
21400    const requestData = { product_id: productId };
21401    if (variantId > 0) {
21402      requestData.variant_id = variantId;
21403    }
21404    
21405    const response = await fetch('/api/compare/add.php', {
21406      method: 'POST',
21407      headers: {
21408        'Content-Type': 'application/json',
21409      },
21410      body: JSON.stringify(requestData),
21411      credentials: 'same-origin'
21412    });
21413    
21414    const result = await response.json();
21415    
21416    if (!result.success) {
21417      throw new Error(result.error || 'Gagal menambahkan produk');
21418    }
21419    
21420    // Send Google Analytics event for add to compare
21421    if (typeof gtag === 'function') {
21422      gtag('event', 'add_to_compare', {
21423        event_category: 'Product Compare',
21424        event_label: 'Product Added to Compare',
21425        product_id: productId,
21426        variant_id: variantId || 0,
21427        custom_map: {
21428          custom_parameter_1: 'compare_add_product'
21429        }
21430      });
21431    }
21432    
21433    await loadCompareList();
21434    return result;
21435  }
21436  
21437  /**
21438   * Remove product from compare list
21439   */
21440  async function removeFromCompare(productId) {
21441    const response = await fetch('/api/compare/remove.php', {
21442      method: 'POST',
21443      headers: {
21444        'Content-Type': 'application/json',
21445      },
21446      body: JSON.stringify({ product_id: productId }),
21447      credentials: 'same-origin'
21448    });
21449    
21450    const result = await response.json();
21451    
21452    if (!result.success) {
21453      throw new Error(result.error || 'Gagal menghapus produk');
21454    }
21455    
21456    // Send Google Analytics event for remove from compare
21457    if (typeof gtag === 'function') {
21458      gtag('event', 'remove_from_compare', {
21459        event_category: 'Product Compare',
21460        event_label: 'Product Removed from Compare',
21461        product_id: productId,
21462        custom_map: {
21463          custom_parameter_1: 'compare_remove_product'
21464        }
21465      });
21466    }
21467    
21468    await loadCompareList();
21469    return result;
21470  }
21471  
21472  /**
21473   * Clear all products from compare list
21474   */
21475  async function clearAllProducts() {
21476    if (compareList.length === 0) return;
21477    
21478    // Show confirmation modal
21479    const confirmed = await showConfirmModal({
21480      title: 'Hapus Semua Produk',
21481      message: 'Apakah Anda yakin ingin menghapus semua produk dari daftar perbandingan?',
21482      confirmText: 'Hapus Semua',
21483      cancelText: 'Batal',
21484      type: 'danger'
21485    });
21486    
21487    if (!confirmed) return;
21488    
21489    try {
21490      const response = await fetch('/api/compare/clear.php', {
21491        method: 'POST',
21492        credentials: 'same-origin'
21493      });
21494      
21495      const result = await response.json();
21496      
21497      if (!result.success) {
21498        throw new Error(result.error || 'Gagal menghapus produk');
21499      }
21500      
21501      // Send Google Analytics event for clear all products
21502      if (typeof gtag === 'function') {
21503        gtag('event', 'compare_clear_all', {
21504          event_category: 'Product Compare',
21505          event_label: 'Clear All Products',
21506          products_count: compareList.length,
21507          custom_map: {
21508            custom_parameter_1: 'compare_clear_action'
21509          }
21510        });
21511      }
21512      
21513      await loadCompareList();
21514      showToast('Semua produk dihapus dari perbandingan', 'success');
21515      
21516    } catch (error) {
21517      showToast(error.message || 'Terjadi kesalahan', 'error');
21518    }
21519  }
21520  
21521  /**
21522   * Load compare list from server
21523   */
21524  async function loadCompareList() {
21525    try {
21526      const response = await fetch('/api/compare/get.php', {
21527        credentials: 'same-origin'
21528      });
21529      
21530      const result = await response.json();
21531      
21532      if (result.success) {
21533        compareList = result.products || [];
21534        updateCompareSidebar();
21535        updateCompareBadge();
21536        updateAllCompareButtons();
21537      }
21538      
21539    } catch (error) {
21540      // Silently handle error - compare functionality will work with empty list
21541    }
21542  }
21543  
21544  /**
21545   * Update compare sidebar content
21546   */
21547  function updateCompareSidebar() {
21548    const emptyState = document.getElementById('compareEmpty');
21549    const listContainer = document.getElementById('compareList');
21550    const actionsContainer = document.getElementById('compareActions');
21551    const countElement = document.getElementById('compareCountSidebar');
21552    
21553    if (!emptyState || !listContainer || !actionsContainer || !countElement) return;
21554    
21555    // Update count
21556    countElement.textContent = `(${compareList.length})`;
21557    
21558    if (compareList.length === 0) {
21559      emptyState.style.display = 'block';
21560      listContainer.style.display = 'none';
21561      actionsContainer.style.display = 'none';
21562    } else {
21563      emptyState.style.display = 'none';
21564      listContainer.style.display = 'block';
21565      actionsContainer.style.display = 'flex';
21566      
21567      // Render product list
21568      listContainer.innerHTML = compareList.map(product => {
21569        // Display variant name if available
21570        let displayName = product.name;
21571        if (product.combination_name) {
21572          displayName += ` (${product.combination_name})`;
21573        }
21574        
21575        // Use variant price if available
21576        let displayPrice = product.price;
21577        if (product.variant_price !== undefined && product.variant_price !== null) {
21578          displayPrice = product.variant_price;
21579        }
21580        
21581        return `
21582          <div class="compare-item" data-product-id="${product.id}">
21583            <div class="compare-item-image">
21584              <img src="${product.image_url}" alt="${escapeHtml(displayName)}" loading="lazy">
21585            </div>
21586            <div class="compare-item-info">
21587              <div class="compare-item-name">${escapeHtml(displayName)}</div>
21588              <div class="compare-item-price">${formatPrice(displayPrice)}</div>
21589            </div>
21590            <button class="compare-item-remove" data-product-id="${product.id}" aria-label="Hapus">
21591              <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
21592                <path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"/>
21593              </svg>
21594            </button>
21595          </div>
21596        `;
21597      }).join('');
21598      
21599      // Setup event listeners for remove buttons
21600      setupRemoveButtons();
21601      
21602      // Update analyze button state
21603      const analyzeBtn = document.getElementById('compareAnalyze');
21604      if (analyzeBtn) {
21605        analyzeBtn.disabled = compareList.length < 2;
21606      }
21607    }
21608  }
21609  
21610  /**
21611   * Setup remove buttons event listeners
21612   */
21613  function setupRemoveButtons() {
21614    const removeButtons = document.querySelectorAll('.compare-item-remove');
21615    removeButtons.forEach(button => {
21616      button.addEventListener('click', async (e) => {
21617        e.preventDefault();
21618        const productId = parseInt(button.dataset.productId);
21619        if (productId) {
21620          await removeProduct(productId);
21621        }
21622      });
21623    });
21624  }
21625
21626  /**
21627   * Update all compare buttons on the page
21628   */
21629  function updateAllCompareButtons() {
21630    // Update product detail compare button
21631    const productBtn = document.getElementById('pd-compare-btn');
21632    if (productBtn) {
21633      const productId = parseInt(productBtn.dataset.productId);
21634      if (productId) {
21635        updateProductCompareButtonState(productBtn, productId);
21636      }
21637    }
21638    
21639    // Update any other compare buttons on the page
21640    const allCompareButtons = document.querySelectorAll('[data-product-id][class*="compare"]');
21641    allCompareButtons.forEach(button => {
21642      const productId = parseInt(button.dataset.productId);
21643      if (productId && button.id !== 'pd-compare-btn') {
21644        updateProductCompareButtonState(button, productId);
21645      }
21646    });
21647  }
21648
21649  /**
21650   * Update compare badge
21651   */
21652  function updateCompareBadge() {
21653    const badge = document.getElementById('compareCount');
21654    if (!badge) return;
21655    
21656    const oldCount = parseInt(badge.textContent) || 0;
21657    const newCount = compareList.length;
21658    
21659    badge.textContent = newCount;
21660    badge.classList.toggle('active', newCount > 0);
21661    
21662    // Animate badge if count increased
21663    if (newCount > oldCount) {
21664      animateCompareBadge();
21665      highlightCompareButton();
21666    }
21667    
21668    // Sticky bar removed - keeping only fly-to-navbar animation
21669  }
21670  
21671  /**
21672   * Animate compare badge when new product added
21673   */
21674  function animateCompareBadge() {
21675    const badge = document.getElementById('compareCount');
21676    if (!badge) return;
21677    
21678    badge.style.transform = 'scale(1.3)';
21679    badge.style.transition = 'transform 0.3s ease';
21680    
21681    setTimeout(() => {
21682      badge.style.transform = 'scale(1)';
21683    }, 300);
21684  }
21685  
21686  /**
21687   * Highlight compare button temporarily
21688   */
21689  function highlightCompareButton() {
21690    const compareBtn = document.getElementById('compareBtn');
21691    if (!compareBtn) return;
21692    
21693    compareBtn.classList.add('pulse-highlight');
21694    
21695    setTimeout(() => {
21696      compareBtn.classList.remove('pulse-highlight');
21697    }, 3000);
21698  }
21699  
21700  /**
21701   * Trigger fly-to-compare animation
21702   */
21703  function triggerFlyToCompareAnimation(sourceButton) {
21704    if (!sourceButton) {
21705      return;
21706    }
21707    
21708    // Check if mobile view (bottom nav) or desktop (top nav)
21709    const isMobile = window.innerWidth <= 768;
21710    
21711    // Get source position
21712    const sourceRect = sourceButton.getBoundingClientRect();
21713    
21714    // Calculate target position
21715    let targetTop, targetLeft;
21716    
21717    if (isMobile) {
21718      // Mobile: fly to fixed bottom position (bottom nav area)
21719      // Use fixed coordinates regardless of bottom nav visibility
21720      targetTop = window.innerHeight - 80; // Just above bottom nav area
21721      targetLeft = window.innerWidth / 2 - 30; // Center horizontally
21722    } else {
21723      // Desktop: fly to top navbar compare button
21724      const targetElement = document.getElementById('compareBtn');
21725      if (!targetElement) {
21726        return;
21727      }
21728      const targetRect = targetElement.getBoundingClientRect();
21729      targetTop = targetRect.top + 10;
21730      targetLeft = targetRect.left + 10;
21731    }
21732    
21733    // Get product image for animation - try multiple selectors
21734    let productImage = document.querySelector('#pd-main-img, .product-gallery img, .pd-image img, .product-image img, .product-main-image img, img[alt*="produk"], img[src*="product"]');
21735    
21736    // Create animated element
21737    const flyingElement = document.createElement('div');
21738    flyingElement.className = 'flying-product';
21739    
21740    if (productImage) {
21741      flyingElement.innerHTML = `<img src="${productImage.src}" alt="Flying product">`;
21742      flyingElement.querySelector('img').style.cssText = `
21743        width: 100%;
21744        height: 100%;
21745        object-fit: cover;
21746        border-radius: 8px;
21747        box-shadow: 0 4px 12px rgba(0,0,0,0.3);
21748      `;
21749    } else {
21750      // Fallback: colored div with icon
21751      flyingElement.innerHTML = `<div class="flying-fallback">📦</div>`;
21752      flyingElement.querySelector('.flying-fallback').style.cssText = `
21753        width: 100%;
21754        height: 100%;
21755        background: #3b82f6;
21756        border-radius: 8px;
21757        display: flex;
21758        align-items: center;
21759        justify-content: center;
21760        font-size: 24px;
21761        box-shadow: 0 4px 12px rgba(0,0,0,0.3);
21762      `;
21763    }
21764    
21765    flyingElement.style.cssText = `
21766      position: fixed;
21767      top: ${sourceRect.top}px;
21768      left: ${sourceRect.left}px;
21769      width: 60px;
21770      height: 60px;
21771      z-index: 9999;
21772      pointer-events: none;
21773      opacity: 1;
21774      transform: scale(1);
21775    `;
21776    
21777    document.body.appendChild(flyingElement);
21778    
21779    // Use Web Animations API for more reliable animation
21780    flyingElement.animate([
21781      // Keyframe 1: Initial state
21782      {
21783        top: `${sourceRect.top}px`,
21784        left: `${sourceRect.left}px`,
21785        transform: 'scale(1)',
21786        opacity: 1
21787      },
21788      // Keyframe 2: Final state
21789      {
21790        top: `${targetTop}px`,
21791        left: `${targetLeft}px`,
21792        transform: 'scale(0.3)',
21793        opacity: 0
21794      }
21795    ], {
21796      duration: 800,
21797      easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
21798      fill: 'forwards'
21799    });
21800    
21801    // Clean up after animation completes
21802    setTimeout(() => {
21803      if (flyingElement.parentNode) {
21804        flyingElement.parentNode.removeChild(flyingElement);
21805      }
21806    }, 850);
21807  }
21808  
21809  // Sticky compare bar functions removed - using only fly-to-navbar animation
21810  
21811  /**
21812   * Toggle compare sidebar
21813   */
21814  function toggleCompareSidebar() {
21815    if (!compareSidebar) return;
21816    
21817    const isOpen = compareSidebar.classList.contains('open');
21818    
21819    if (isOpen) {
21820      closeCompareSidebar();
21821    } else {
21822      openCompareSidebar();
21823    }
21824  }
21825  
21826  /**
21827   * Open compare sidebar
21828   */
21829  function openCompareSidebar() {
21830    if (!compareSidebar || !compareOverlay) return;
21831    
21832    compareSidebar.classList.add('open');
21833    compareOverlay.classList.add('active');
21834    document.body.style.overflow = 'hidden';
21835    
21836    // Send Google Analytics event for compare sidebar open
21837    if (typeof gtag === 'function') {
21838      gtag('event', 'compare_sidebar_open', {
21839        event_category: 'Product Compare',
21840        event_label: 'Compare Sidebar Opened',
21841        products_count: compareList.length,
21842        custom_map: {
21843          custom_parameter_1: 'compare_sidebar_view'
21844        }
21845      });
21846    }
21847  }
21848  
21849  /**
21850   * Close compare sidebar
21851   */
21852  function closeCompareSidebar() {
21853    if (!compareSidebar || !compareOverlay) return;
21854    
21855    compareSidebar.classList.remove('open');
21856    compareOverlay.classList.remove('active');
21857    document.body.style.overflow = '';
21858  }
21859  
21860  /**
21861   * Start AI analysis
21862   */
21863  async function startAIAnalysis() {
21864    if (compareList.length < 2) {
21865      showToast('Minimal 2 produk diperlukan untuk analisis AI', 'warning');
21866      return;
21867    }
21868    
21869    if (isAnalyzing) return;
21870    
21871    // Send Google Analytics event for AI Analysis start
21872    if (typeof gtag === 'function') {
21873      gtag('event', 'ai_analysis_start', {
21874        event_category: 'Product Compare',
21875        event_label: 'AI Analysis Button Click',
21876        products_count: compareList.length,
21877        product_ids: compareList.map(p => p.id).join(','),
21878        custom_map: {
21879          custom_parameter_1: 'compare_ai_analysis'
21880        }
21881      });
21882    }
21883    
21884    // Check if products are in similar categories
21885    const mismatchInfo = await checkProductCategoryMismatch();
21886    if (mismatchInfo.shouldWarn) {
21887      const categoryList = Array.from(mismatchInfo.categories).join(', ');
21888      const confirmed = await showConfirmModal({
21889        title: 'Produk Berbeda Kategori',
21890        message: `Produk yang Anda pilih berasal dari kategori yang berbeda: ${categoryList}. AI tetap bisa menganalisis, tapi perbandingan mungkin kurang relevan. Lanjutkan analisis?`,
21891        confirmText: 'Ya, Analisis Tetap',
21892        cancelText: 'Batal',
21893        type: 'warning'
21894      });
21895      
21896      if (!confirmed) return;
21897    }
21898    
21899    openModal();
21900    showLoadingState();
21901    isAnalyzing = true;
21902    
21903    try {
21904      const response = await fetch('/api/compare/ai-analysis.php', {
21905        method: 'POST',
21906        credentials: 'same-origin'
21907      });
21908      
21909      const result = await response.json();
21910      
21911      if (!result.success) {
21912        throw new Error(result.error || 'Analisis AI gagal');
21913      }
21914      
21915      currentAnalysis = result;
21916      showAnalysisResults(result);
21917      
21918      // Send Google Analytics event for successful AI Analysis
21919      if (typeof gtag === 'function') {
21920        gtag('event', 'ai_analysis_success', {
21921          event_category: 'Product Compare',
21922          event_label: 'AI Analysis Completed',
21923          products_count: compareList.length,
21924          analysis_type: 'product_comparison',
21925          custom_map: {
21926            custom_parameter_1: 'compare_ai_success'
21927          }
21928        });
21929      }
21930      
21931    } catch (error) {
21932      showErrorState(error.message);
21933      
21934      // Send Google Analytics event for failed AI Analysis
21935      if (typeof gtag === 'function') {
21936        gtag('event', 'ai_analysis_error', {
21937          event_category: 'Product Compare',
21938          event_label: 'AI Analysis Failed',
21939          products_count: compareList.length,
21940          error_message: error.message,
21941          custom_map: {
21942            custom_parameter_1: 'compare_ai_error'
21943          }
21944        });
21945      }
21946    } finally {
21947      isAnalyzing = false;
21948    }
21949  }
21950  
21951  /**
21952   * Show modal
21953   */
21954  function openModal() {
21955    if (!compareModal) return;
21956    compareModal.classList.add('open');
21957    document.body.style.overflow = 'hidden';
21958  }
21959  
21960  /**
21961   * Close modal
21962   */
21963  function closeModal() {
21964    if (!compareModal) return;
21965    compareModal.classList.remove('open');
21966    document.body.style.overflow = '';
21967  }
21968  
21969  /**
21970   * Show loading state
21971   */
21972  function showLoadingState() {
21973    const loading = document.getElementById('aiLoading');
21974    const error = document.getElementById('aiError');
21975    const results = document.getElementById('aiResults');
21976    
21977    if (loading) loading.style.display = 'block';
21978    if (error) error.hidden = true;
21979    if (results) results.hidden = true;
21980  }
21981  
21982  /**
21983   * Show error state
21984   */
21985  function showErrorState(message) {
21986    const loading = document.getElementById('aiLoading');
21987    const error = document.getElementById('aiError');
21988    const results = document.getElementById('aiResults');
21989    const errorMessage = document.getElementById('aiErrorMessage');
21990    
21991    if (loading) loading.style.display = 'none';
21992    if (error) error.hidden = false;
21993    if (results) results.hidden = true;
21994    if (errorMessage) errorMessage.textContent = message;
21995  }
21996  
21997  /**
21998   * Show analysis results
21999   */
22000  function showAnalysisResults(analysis) {
22001    const loading = document.getElementById('aiLoading');
22002    const error = document.getElementById('aiError');
22003    const results = document.getElementById('aiResults');
22004    
22005    if (loading) loading.style.display = 'none';
22006    if (error) error.hidden = true;
22007    if (results) results.hidden = false;
22008    
22009    const data = analysis.data;
22010    
22011    // Update summary
22012    const summaryElement = document.getElementById('aiSummary');
22013    if (summaryElement) summaryElement.textContent = data.summary || '';
22014    
22015    const winnerProduct = document.getElementById('winnerProduct');
22016    const winnerReason = document.getElementById('winnerReason');
22017    if (winnerProduct && winnerReason && data.winner_overall) {
22018      const winner = compareList.find(p => parseInt(p.id) === parseInt(data.winner_overall));
22019      winnerProduct.textContent = winner ? winner.name : 'Tidak diketahui';
22020      winnerReason.textContent = data.winner_reason || '';
22021    }
22022    
22023    // Update comparison table
22024    updateComparisonTable(data.comparison_table);
22025    
22026    // Update detailed analysis
22027    updateDetailedAnalysis(data.detailed_analysis);
22028    
22029    // Update recommendations
22030    updateRecommendations(data.recommendations);
22031    
22032    // Update buying tips
22033    updateBuyingTips(data.buying_tips);
22034  }
22035  
22036  /**
22037   * Update comparison table
22038   */
22039  function updateComparisonTable(comparisonData) {
22040    const container = document.getElementById('comparisonTable');
22041    if (!container || !comparisonData?.categories) return;
22042    
22043    // First, create product header row with names
22044    const productHeaderHtml = `
22045      <div class="comparison-category">
22046        <div class="category-header">Produk</div>
22047        <div class="category-products">
22048          ${compareList.map(product => `
22049            <div class="product-score">
22050              <div class="product-name-header">
22051                <strong>${escapeHtml(product.name)}</strong>
22052              </div>
22053              <div class="product-price">
22054                ${formatPrice(product.price)}
22055              </div>
22056            </div>
22057          `).join('')}
22058        </div>
22059      </div>
22060    `;
22061    
22062    // Then create comparison categories
22063    const categoriesHtml = comparisonData.categories.map(category => `
22064      <div class="comparison-category">
22065        <div class="category-header">${escapeHtml(category.name)}</div>
22066        <div class="category-products">
22067          ${compareList.map(product => {
22068            const productData = category.products?.[product.id];
22069            return `
22070              <div class="product-score">
22071                <div class="score-value">
22072                  <span class="score-stars">${'★'.repeat(productData?.score || 0)}${'☆'.repeat(5 - (productData?.score || 0))}</span>
22073                  <span>${productData?.score || 0}/5</span>
22074                </div>
22075                <div class="score-note">${escapeHtml(productData?.note || '')}</div>
22076              </div>
22077            `;
22078          }).join('')}
22079        </div>
22080      </div>
22081    `).join('');
22082    
22083    container.innerHTML = productHeaderHtml + categoriesHtml;
22084  }
22085  
22086  /**
22087   * Update detailed analysis
22088   */
22089  function updateDetailedAnalysis(analysisData) {
22090    const container = document.getElementById('detailedAnalysis');
22091    if (!container || !analysisData) return;
22092    
22093    container.innerHTML = compareList.map(product => {
22094      const productAnalysis = analysisData[product.id];
22095      if (!productAnalysis) return '';
22096      
22097      // Use product name from API response if available, fallback to local name
22098      const productName = productAnalysis.product_name || product.name;
22099      
22100      return `
22101        <div class="product-analysis">
22102          <h5>${escapeHtml(productName)}</h5>
22103          
22104          ${productAnalysis.strengths ? `
22105            <div class="analysis-section">
22106              <h6 class="strengths">✅ Kelebihan</h6>
22107              <ul>
22108                ${productAnalysis.strengths.map(strength => 
22109                  `<li>${escapeHtml(strength)}</li>`
22110                ).join('')}
22111              </ul>
22112            </div>
22113          ` : ''}
22114          
22115          ${productAnalysis.weaknesses ? `
22116            <div class="analysis-section">
22117              <h6 class="weaknesses">❌ Kekurangan</h6>
22118              <ul>
22119                ${productAnalysis.weaknesses.map(weakness => 
22120                  `<li>${escapeHtml(weakness)}</li>`
22121                ).join('')}
22122              </ul>
22123            </div>
22124          ` : ''}
22125          
22126          ${productAnalysis.best_for ? `
22127            <div class="analysis-section">
22128              <h6 class="best-for">🎯 Cocok Untuk</h6>
22129              <p class="best-for-text">${escapeHtml(productAnalysis.best_for)}</p>
22130            </div>
22131          ` : ''}
22132        </div>
22133      `;
22134    }
22134).filter(Boolean).join('');
22135  }
22136  
22137  /**
22138   * Update recommendations
22139   */
22140  function updateRecommendations(recommendations) {
22141    if (!recommendations) return;
22142    
22143    const budgetEl = document.getElementById('budgetRecommendation');
22144    const performanceEl = document.getElementById('performanceRecommendation');
22145    const balancedEl = document.getElementById('balancedRecommendation');
22146    
22147    if (budgetEl && recommendations.budget_conscious) {
22148      const product = compareList.find(p => parseInt(p.id) === parseInt(recommendations.budget_conscious));
22149      budgetEl.textContent = product ? product.name : 'Tidak tersedia';
22150    }
22151    
22152    if (performanceEl && recommendations.performance_focused) {
22153      const product = compareList.find(p => parseInt(p.id) === parseInt(recommendations.performance_focused));
22154      performanceEl.textContent = product ? product.name : 'Tidak tersedia';
22155    }
22156    
22157    if (balancedEl && recommendations.balanced) {
22158      const product = compareList.find(p => parseInt(p.id) === parseInt(recommendations.balanced));
22159      balancedEl.textContent = product ? product.name : 'Tidak tersedia';
22160    }
22161  }
22162  
22163  /**
22164   * Check if products are from different categories
22165   */
22166  async function checkProductCategoryMismatch() {
22167    if (compareList.length < 2) {
22168      return { shouldWarn: false, categories: new Set() };
22169    }
22170    
22171    // Get the root categories from each product
22172    const rootCategories = new Set();
22173    const productCategories = [];
22174    
22175    for (const product of compareList) {
22176      // Extract root category from the product's category_path if available
22177      const rootCategory = await getProductRootCategory(product);
22178      if (rootCategory) {
22179        rootCategories.add(rootCategory);
22180        productCategories.push({
22181          product: product.name,
22182          category: rootCategory
22183        });
22184      }
22185    }
22186    
22187    // If we have more than 1 different root categories, show warning
22188    const shouldWarn = rootCategories.size > 1 && compareList.length >= 2;
22189    
22190    return {
22191      shouldWarn: shouldWarn,
22192      categories: rootCategories,
22193      productCategories: productCategories
22194    };
22195  }
22196  
22197  /**
22198   * Get product root category from category_path or fallback to name detection
22199   */
22200  async function getProductRootCategory(product) {
22201    // First try to get from category_path if available
22202    if (product.category_path) {
22203      const rootCategory = product.category_path.split('/')[0];
22204      return rootCategory;
22205    }
22206    
22207    // If no category_path, try to fetch from product detail or use name detection
22208    const detectedCategory = detectProductCategory(product.name);
22209    return detectedCategory;
22210  }
22211
22212  /**
22213   * Detect product category from name (fallback method)
22214   */
22215  function detectProductCategory(productName) {
22216    const name = productName.toLowerCase();
22217    
22218    // Map keywords to actual root categories from the API structure
22219    const categoryMappings = {
22220      // Komputer & Laptop
22221      'Komputer & Laptop': ['laptop', 'notebook', 'macbook', 'thinkpad', 'gaming laptop', 'desktop', 'pc', 'mini pc', 'all-in-one', 'server'],
22222      
22223      // Printer & Scanner  
22224      'Printer & Scanner': ['printer', 'epson printer', 'canon printer', 'hp printer', 'brother printer', 'scanner', 'laser printer', 'inkjet', 'dot matrix'],
22225      
22226      // Gadget
22227      'Gadget': ['smartphone', 'iphone', 'samsung phone', 'xiaomi', 'oppo', 'vivo', 'realme', 'tablet', 'ipad', 'android tablet'],
22228      
22229      // Periferal
22230      'Periferal': ['monitor', 'lcd monitor', 'led monitor', 'gaming monitor', 'keyboard', 'mouse', 'gaming keyboard', 'mechanical keyboard', 'webcam'],
22231      
22232      // Audio
22233      'Audio': ['headset', 'headphone', 'earphone', 'gaming headset', 'speaker', 'mikrofon', 'sound card'],
22234      
22235      // Jaringan
22236      'Jaringan': ['router', 'wifi', 'access point', 'switch', 'modem', 'network'],
22237      
22238      // Penyimpanan
22239      'Penyimpanan': ['ssd', 'hdd', 'nvme', 'solid state', 'hard disk', 'flash drive', 'usb drive', 'memory card'],
22240      
22241      // Komponen PC
22242      'Komponen PC': ['ram', 'memory', 'ddr4', 'ddr5', 'processor', 'cpu', 'intel', 'amd', 'motherboard', 'graphics card', 'gpu', 'psu', 'power supply', 'casing'],
22243      
22244      // UPS & Power
22245      'UPS & Power': ['ups', 'uninterruptible', 'stabilizer', 'power supply'],
22246      
22247      // Proyektor
22248      'Proyektor': ['proyektor', 'projector', 'layar proyektor'],
22249      
22250      // Security System
22251      'Security System': ['cctv', 'security camera', 'surveillance'],
22252      
22253      // Aksesoris
22254      'Aksesoris': ['charger laptop', 'adaptor', 'converter', 'dock', 'hub', 'kabel', 'cable', 'power bank', 'tas laptop'],
22255      
22256      // Software
22257      'Software': ['software', 'lisensi', 'license', 'antivirus'],
22258      
22259      // Perlengkapan Kantor
22260      'Perlengkapan Kantor': ['fingerprint', 'finger print', 'atk', 'alat tulis']
22261    };
22262    
22263    // Check which root category matches
22264    for (const [rootCategory, keywords] of Object.entries(categoryMappings)) {
22265      for (const keyword of keywords) {
22266        if (name.includes(keyword)) {
22267          return rootCategory;
22268        }
22269      }
22270    }
22271    
22272    return null;
22273  }
22274
22275  /**
22276   * Update buying tips
22277   */
22278  function updateBuyingTips(tips) {
22279    const container = document.getElementById('buyingTips');
22280    if (!container || !Array.isArray(tips)) return;
22281    
22282    container.innerHTML = tips.map(tip => 
22283      `<li>${escapeHtml(tip)}</li>`
22284    ).join('');
22285  }
22286  
22287  // Removed share and cart functions - replaced with AI disclaimer in UI
22288  
22289  /**
22290   * Remove product from compare (public method)
22291   */
22292  async function removeProduct(productId) {
22293    try {
22294      await removeFromCompare(productId);
22295      showToast('Produk dihapus dari perbandingan', 'success');
22296      
22297      // Update all compare buttons
22298      updateAllCompareButtons();
22299      
22300    } catch (error) {
22301      showToast(error.message || 'Terjadi kesalahan', 'error');
22302    }
22303  }
22304  
22305  /**
22306   * Utility functions
22307   */
22308  function escapeHtml(text) {
22309    const div = document.createElement('div');
22310    div.textContent = text;
22311    return div.innerHTML;
22312  }
22313  
22314  function formatPrice(price) {
22315    return 'Rp ' + parseInt(price).toLocaleString('id-ID');
22316  }
22317  
22318  /**
22319   * Show confirmation modal using the existing modal.css design
22320   */
22321  function showConfirmModal(options = {}) {
22322    return new Promise((resolve) => {
22323      const {
22324        title = 'Konfirmasi',
22325        message = 'Apakah Anda yakin?',
22326        confirmText = 'Ya',
22327        cancelText = 'Tidak',
22328        type = 'warning'
22329      } = options;
22330      
22331      // Create modal HTML
22332      const modalHTML = `
22333        <div class="modal-overlay" id="confirmModal">
22334          <div class="modal-dialog">
22335            <div class="modal-header">
22336              <h3 class="modal-title">
22337                <div class="modal-icon ${type}">
22338                  ${getModalIcon(type)}
22339                </div>
22340                ${escapeHtml(title)}
22341              </h3>
22342            </div>
22343            <div class="modal-body">
22344              <p class="modal-message">${escapeHtml(message)}</p>
22345              <div class="modal-actions">
22346                <button type="button" class="modal-btn modal-btn-secondary" id="confirmCancel">
22347                  ${escapeHtml(cancelText)}
22348                </button>
22349                <button type="button" class="modal-btn modal-btn-${type === 'danger' ? 'danger' : 'primary'}" id="confirmOk">
22350                  ${escapeHtml(confirmText)}
22351                </button>
22352              </div>
22353            </div>
22354          </div>
22355        </div>
22356      `;
22357      
22358      // Add to DOM
22359      document.body.insertAdjacentHTML('beforeend', modalHTML);
22360      const modal = document.getElementById('confirmModal');
22361      const cancelBtn = document.getElementById('confirmCancel');
22362      const okBtn = document.getElementById('confirmOk');
22363      
22364      // Show modal
22365      requestAnimationFrame(() => {
22366        modal.classList.add('show');
22367      });
22368      
22369      // Handle events
22370      function cleanup() {
22371        modal.remove();
22372      }
22373      
22374      function handleCancel() {
22375        modal.classList.remove('show');
22376        setTimeout(() => {
22377          cleanup();
22378          resolve(false);
22379        }, 200);
22380      }
22381      
22382      function handleConfirm() {
22383        modal.classList.remove('show');
22384        setTimeout(() => {
22385          cleanup();
22386          resolve(true);
22387        }, 200);
22388      }
22389      
22390      cancelBtn.addEventListener('click', handleCancel);
22391      okBtn.addEventListener('click', handleConfirm);
22392      
22393      // Close on overlay click
22394      modal.addEventListener('click', (e) => {
22395        if (e.target === modal) {
22396          handleCancel();
22397        }
22398      });
22399      
22400      // Close on escape
22401      document.addEventListener('keydown', function escapeHandler(e) {
22402        if (e.key === 'Escape') {
22403          document.removeEventListener('keydown', escapeHandler);
22404          handleCancel();
22405        }
22406      });
22407      
22408      // Focus on confirm button
22409      setTimeout(() => okBtn.focus(), 100);
22410    });
22411  }
22412  
22413  /**
22414   * Get icon for modal type
22415   */
22416  function getModalIcon(type) {
22417      const icons = {
22418        danger: '🚨 ',
22419        warning: '⚠️',
22420        success: '✅',
22421        info: 'ℹ️'
22422      };
22423      return icons[type] || icons.info;
22424    }
22425
22426
22427  function showToast(message, type = 'info') {
22428    // Integration with existing toast system or create simple one
22429    
22430    // Simple alert fallback - replace with proper toast system
22431    if (type === 'error') {
22432      alert('Error: ' + message);
22433    }
22434  }
22435  
22436  // Inline compare preview functions removed - keeping it simple and clean
22437  
22438  /**
22439   * Get current product ID from page
22440   */
22441  function getCurrentProductId() {
22442    const button = document.getElementById('pd-compare-btn');
22443    return button ? parseInt(button.dataset.productId) : null;
22444  }
22445  
22446  // hideStickyCompareBar function removed - no longer using sticky bar
22447
22448  // Public API
22449  window.productCompare = {
22450    removeProduct: removeProduct,
22451    addToCompare: addToCompare,
22452    loadCompareList: loadCompareList,
22453    getCompareList: function() { return compareList; },
22454    openSidebar: openCompareSidebar,
22455    startAnalysis: startAIAnalysis,
22456    // Helper function to add from external sources with variant support
22457    addProductWithVariant: async function(productId, variantId = 0) {
22458      return await addToCompare(productId, variantId);
22459    }
22460  };
22461
22462})();
22463
22464
22465
22466</script>
vendor: 1 bytes, line 22466
22466
22467<script data-inlined="product-card-actions.js">
22468/**
22469 * Product Card Quick Actions
22470 * Bottom sheet menu for quick add to cart & compare
22471 * Integrates with existing product-compare.js and cart.js
22472 */
22473
22474(function() {
22475  'use strict';
22476  
22477  let activeSheet = null;
22478  let activeProductData = null;
22479  
22480  // DOM elements (will be created dynamically)
22481  let overlay = null;
22482  let bottomSheet = null;
22483  
22484  /**
22485   * Initialize on DOM ready
22486   */
22487  document.addEventListener('DOMContentLoaded', function() {
22488    createBottomSheet();
22489    attachEventListeners();
22490  });
22491  
22492  /**
22493   * Create bottom sheet HTML structure
22494   */
22495  function createBottomSheet() {
22496    // Create overlay
22497    overlay = document.createElement('div');
22498    overlay.className = 'pca-overlay';
22499    overlay.addEventListener('click', closeBottomSheet);
22500    
22501    // Create bottom sheet
22502    bottomSheet = document.createElement('div');
22503    bottomSheet.className = 'pca-bottom-sheet';
22504    bottomSheet.innerHTML = `
22505      <div class="pca-sheet-handle"></div>
22506      <div class="pca-sheet-product" id="pca-product-preview">
22507        <!-- Product info will be inserted here -->
22508      </div>
22509      <div class="pca-sheet-actions">
22510        <button type="button" class="pca-action-item cart" id="pca-add-cart">
22511          <svg class="pca-action-icon" viewBox="0 0 24 24" fill="currentColor">
22512            <path d="M17,18A2,2 0 0,1 19,20A2,2 0 0,1 17,22C15.89,22 15,21.1 15,20C15,18.89 15.89,18 17,18M1,2H4.27L5.21,4H20A1,1 0 0,1 21,5C21,5.17 20.95,5.34 20.88,5.5L17.3,11.97C16.96,12.58 16.3,13 15.55,13H8.1L7.2,14.63L7.17,14.75A0.25,0.25 0 0,0 7.42,15H19V17H7C5.89,17 5,16.1 5,15C5,14.65 5.09,14.32 5.24,14.04L6.6,11.59L3,4H1V2M7,18A2,2 0 0,1 9,20A2,2 0 0,1 7,22C5.89,22 5,21.1 5,20C5,18.89 5.89,18 7,18M16,11L18.78,6H6.14L8.5,11H16Z"/>
22513          </svg>
22514          <div class="pca-action-content">
22515            <div class="pca-action-label">Tambah ke Keranjang</div>
22516            <div class="pca-action-desc">Lanjut belanja produk lain</div>
22517          </div>
22518        </button>
22519        
22520        <button type="button" class="pca-action-item compare" id="pca-add-compare">
22521          <svg class="pca-action-icon" viewBox="0 0 24 24" fill="currentColor">
22522            <path d="M21,9L17,5V8H10V10H17V13M7,11L3,15L7,19V16H14V14H7V11Z"/>
22523          </svg>
22524          <div class="pca-action-content">
22525            <div class="pca-action-label">Bandingkan Produk</div>
22526            <div class="pca-action-desc">Analisis dengan AI</div>
22527          </div>
22528          <span class="pca-action-badge compare" id="pca-compare-count" style="display:none;">0</span>
22529        </button>
22530      </div>
22531    `;
22532    
22533    // Append to body
22534    document.body.appendChild(overlay);
22535    document.body.appendChild(bottomSheet);
22536    
22537    // Setup action buttons
22538    document.getElementById('pca-add-cart').addEventListener('click', handleAddToCart);
22539    document.getElementById('pca-add-compare').addEventListener('click', handleAddToCompare);
22540    
22541    // Prevent clicks inside bottom sheet from closing it
22542    bottomSheet.addEventListener('click', function(e) {
22543      e.stopPropagation();
22544    });
22545  }
22546  
22547  /**
22548   * Attach event listeners to all 3-dot buttons
22549   */
22550  function attachEventListeners() {
22551    // Use event delegation for dynamically loaded product cards
22552    document.addEventListener('click', function(e) {
22553      const btn = e.target.closest('.prod-card-actions-btn');
22554      if (btn) {
22555        e.preventDefault();
22556        e.stopPropagation();
22557        handleActionsButtonClick(btn);
22558      }
22559    });
22560    
22561    // Handle ESC key
22562    document.addEventListener('keydown', function(e) {
22563      if (e.key === 'Escape' && activeSheet) {
22564        closeBottomSheet();
22565      }
22566    });
22567  }
22568  
22569  /**
22570   * Handle 3-dot button click
22571   */
22572  function handleActionsButtonClick(button) {
22573    // Get product ID directly from button
22574    const productId = parseInt(button.dataset.productId) || 0;
22575    if (!productId) {
22576      console.error('Product ID not found on button:', button);
22577      showToast('Data produk tidak ditemukan', 'error');
22578      return;
22579    }
22580    
22581    // Find parent card for additional data
22582    const card = button.closest('.prod-card, .pa-card');
22583    if (!card) {
22584      console.error('Product card not found');
22585      showToast('Data produk tidak ditemukan', 'error');
22586      return;
22587    }
22588    
22589    // Extract product data from card
22590    const productData = extractProductData(card, productId);
22591    if (!productData) {
22592      showToast('Data produk tidak ditemukan', 'error');
22593      return;
22594    }
22595    
22596    activeProductData = productData;
22597    openBottomSheet(productData);
22598  }
22599  
22600  /**
22601   * Extract product data from card element
22602   */
22603  function extractProductData(card, productId) {
22604    try {
22605      // Product ID already passed from button
22606      if (!productId) {
22607        console.error('Product ID not provided');
22608        return null;
22609      }
22610      // Product ID already passed from button
22611      if (!productId) {
22612        console.error('Product ID not provided');
22613        return null;
22614      }
22615      
22616      const id = productId;
22617      
22618      // Get product name
22619      const nameEl = card.querySelector('.prod-name, .title');
22620      const name = nameEl ? nameEl.textContent.trim() : 'Produk';
22621      
22622      // Get product image
22623      const imgEl = card.querySelector('.thumb img, img');
22624      const image = imgEl ? imgEl.src : '/assets/img/placeholder.png';
22625      
22626      // Get product price and detect variants
22627      const priceEl = card.querySelector('.prod-price-final, .final, .prod-price');
22628      let price = 0;
22629      let hasVariants = false;
22630      let priceDisplay = '';
22631      
22632      if (priceEl) {
22633        // Get text content and clean it
22634        let priceText = priceEl.textContent.trim();
22635        priceDisplay = priceText;
22636        
22637        // Check if it's a range (indicates product has variants)
22638        if (priceText.includes('-')) {
22639          hasVariants = true;
22640          // Take the first price for calculation
22641          priceText = priceText.split('-')[0].trim();
22642        }
22643        
22644        // Remove all non-numeric characters
22645        priceText = priceText.replace(/[^0-9]/g, '');
22646        price = parseInt(priceText) || 0;
22647      }
22648      
22649      // Get stock status
22650      const stockDot = card.querySelector('.stock-dot');
22651      let stockStatus = 'unknown';
22652      let stockLabel = 'Cek stok';
22653      
22654      if (stockDot) {
22655        if (stockDot.classList.contains('ready')) {
22656          stockStatus = 'in';
22657          stockLabel = 'Stok tersedia';
22658        } else if (stockDot.classList.contains('low')) {
22659          stockStatus = 'low';
22660          stockLabel = 'Stok terbatas';
22661        } else if (stockDot.classList.contains('out')) {
22662          stockStatus = 'out';
22663          stockLabel = 'Stok habis';
22664        }
22665      }
22666      
22667      // Get category path (if available)
22668      const categoryPath = card.dataset.category || '';
22669      
22670      // Get URL
22671      let url = '#';
22672      if (card.tagName === 'A') {
22673        url = card.href;
22674      } else {
22675        const link = card.querySelector('a[href*="/product/"]');
22676        if (link) url = link.href;
22677      }
22678      
22679      return {
22680        id: id,
22681        name: name,
22682        image: image,
22683        price: price,
22684        priceDisplay: priceDisplay,
22685        hasVariants: hasVariants,
22686        stockStatus: stockStatus,
22687        stockLabel: stockLabel,
22688        categoryPath: categoryPath,
22689        url: url
22690      };
22691    } catch (error) {
22692      console.error('Error extracting product data:', error);
22693      return null;
22694    }
22695  }
22696  
22697  /**
22698   * Open bottom sheet
22699   */
22700  function openBottomSheet(productData) {
22701    if (!overlay || !bottomSheet) return;
22702    
22703    // Update product preview
22704    updateProductPreview(productData);
22705    
22706    // Update cart button based on variant status
22707    updateCartButtonForVariant(productData);
22708    
22709    // Update compare button state
22710    updateCompareButtonState(productData.id);
22711    
22712    // Show overlay and sheet
22713    overlay.classList.add('active');
22714    bottomSheet.classList.add('active');
22715    document.body.style.overflow = 'hidden';
22716    
22717    activeSheet = true;
22718    
22719    // Google Analytics
22720    if (typeof gtag === 'function') {
22721      gtag('event', 'quick_actions_open', {
22722        event_category: 'Product Card',
22723        event_label: 'Quick Actions Menu Opened',
22724        product_id: productData.id,
22725        product_name: productData.name
22726      });
22727    }
22728  }
22729  
22730  /**
22731   * Close bottom sheet
22732   */
22733  function closeBottomSheet() {
22734    if (!overlay || !bottomSheet) return;
22735    
22736    overlay.classList.remove('active');
22737    bottomSheet.classList.remove('active');
22738    document.body.style.overflow = '';
22739    
22740    activeSheet = false;
22741    activeProductData = null;
22742  }
22743  
22744  /**
22745   * Update product preview in bottom sheet
22746   */
22747  function updateProductPreview(productData) {
22748    const preview = document.getElementById('pca-product-preview');
22749    if (!preview) return;
22750    
22751    // Use full price display (shows range for variants)
22752    const priceDisplay = productData.priceDisplay || formatPrice(productData.price);
22753    
22754    preview.innerHTML = `
22755      <img src="${escapeHtml(productData.image)}" 
22756           alt="${escapeHtml(productData.name)}" 
22757           class="pca-product-image"
22758           loading="lazy">
22759      <div class="pca-product-info">
22760        <h4 class="pca-product-name">${escapeHtml(productData.name)}</h4>
22761        <p class="pca-product-price">${escapeHtml(priceDisplay)}</p>
22762        <p class="pca-product-stock ${productData.stockStatus}">${productData.stockLabel}</p>
22763      </div>
22764    `;
22765  }
22766  
22767  /**
22768   * Update cart button for variant products
22769   */
22770  function updateCartButtonForVariant(productData) {
22771    const cartBtn = document.getElementById('pca-add-cart');
22772    if (!cartBtn) return;
22773    
22774    if (productData.hasVariants) {
22775      // Product has variants - change to "View Detail"
22776      cartBtn.dataset.hasVariants = 'true';
22777      cartBtn.dataset.productUrl = productData.url;
22778      
22779      // Update icon to eye/external
22780      const iconSvg = cartBtn.querySelector('svg');
22781      if (iconSvg) {
22782        iconSvg.innerHTML = '<path d="M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z"/>';
22783      }
22784      
22785      // Update text
22786      const label = cartBtn.querySelector('.pca-action-label');
22787      const desc = cartBtn.querySelector('.pca-action-desc');
22788      if (label) label.textContent = 'Lihat Detail Produk';
22789      if (desc) desc.textContent = 'Pilih varian terlebih dahulu';
22790      
22791    } else {
22792      // Single product - normal cart button
22793      cartBtn.dataset.hasVariants = 'false';
22794      
22795      // Restore cart icon
22796      const iconSvg = cartBtn.querySelector('svg');
22797      if (iconSvg) {
22798        iconSvg.innerHTML = '<path d="M17,18A2,2 0 0,1 19,20A2,2 0 0,1 17,22C15.89,22 15,21.1 15,20C15,18.89 15.89,18 17,18M1,2H4.27L5.21,4H20A1,1 0 0,1 21,5C21,5.17 20.95,5.34 20.88,5.5L17.3,11.97C16.96,12.58 16.3,13 15.55,13H8.1L7.2,14.63L7.17,14.75A0.25,0.25 0 0,0 7.42,15H19V17H7C5.89,17 5,16.1 5,15C5,14.65 5.09,14.32 5.24,14.04L6.6,11.59L3,4H1V2M7,18A2,2 0 0,1 9,20A2,2 0 0,1 7,22C5.89,22 5,21.1 5,20C5,18.89 5.89,18 7,18M16,11L18.78,6H6.14L8.5,11H16Z"/>';
22799      }
22800      
22801      // Restore text
22802      const label = cartBtn.querySelector('.pca-action-label');
22803      const desc = cartBtn.querySelector('.pca-action-desc');
22804      if (label) label.textContent = 'Tambah ke Keranjang';
22805      if (desc) desc.textContent = 'Lanjut belanja produk lain';
22806    }
22807  }
22808  
22809  /**
22810   * Update compare button state
22811   */
22812  function updateCompareButtonState(productId) {
22813    const compareBtn = document.getElementById('pca-add-compare');
22814    const compareBadge = document.getElementById('pca-compare-count');
22815    
22816    if (!compareBtn) return;
22817    
22818    // Check if product is in compare list (using global compareList if available)
22819    let isInCompare = false;
22820    let compareCount = 0;
22821    
22822    // Try to get compare list from product-compare.js via window.productCompare
22823    if (typeof window.productCompare !== 'undefined' && typeof window.productCompare.getCompareList === 'function') {
22824      const compareList = window.productCompare.getCompareList();
22825      compareCount = compareList.length;
22826      isInCompare = compareList.some(p => parseInt(p.id) === parseInt(productId));
22827    } else {
22828      // Fallback: check badge count
22829      const mainBadge = document.getElementById('compareCount');
22830      if (mainBadge) {
22831        compareCount = parseInt(mainBadge.textContent) || 0;
22832      }
22833    }
22834    
22835    // Update button state
22836    if (isInCompare) {
22837      compareBtn.classList.add('active');
22838      compareBtn.querySelector('.pca-action-label').textContent = 'Hapus dari Perbandingan';
22839      compareBtn.querySelector('.pca-action-desc').textContent = 'Sudah ada di daftar';
22840    } else {
22841      compareBtn.classList.remove('active');
22842      compareBtn.querySelector('.pca-action-label').textContent = 'Bandingkan Produk';
22843      compareBtn.querySelector('.pca-action-desc').textContent = 'Analisis dengan AI';
22844    }
22845    
22846    // Update badge
22847    if (compareBadge && compareCount > 0) {
22848      compareBadge.textContent = compareCount;
22849      compareBadge.style.display = 'inline-flex';
22850    } else if (compareBadge) {
22851      compareBadge.style.display = 'none';
22852    }
22853  }
22854  
22855  /**
22856   * Handle add to cart
22857   */
22858  async function handleAddToCart() {
22859    if (!activeProductData) return;
22860    
22861    const button = document.getElementById('pca-add-cart');
22862    if (!button) return;
22863    
22864    // If product has variants, redirect to detail page
22865    if (activeProductData.hasVariants) {
22866      window.location.href = activeProductData.url;
22867      return;
22868    }
22869    
22870    // Check stock
22871    if (activeProductData.stockStatus === 'out') {
22872      showToast('Produk sedang habis', 'warning');
22873      return;
22874    }
22875    
22876    // Show loading state
22877    button.classList.add('loading');
22878    button.disabled = true;
22879    
22880    // CSRF Token Helper
22881    function getCsrfToken() {
22882      return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
22883    }
22884    
22885    try {
22886      // Call cart API with proper format
22887      const response = await fetch('/api/cart/add.php', {
22888        method: 'POST',
22889        headers: {
22890          'Content-Type': 'application/json',
22891          'X-CSRF-Token': getCsrfToken()
22892        },
22893        body: JSON.stringify({
22894          csrf_token: getCsrfToken(),
22895          product_id: activeProductData.id,
22896          qty: 1, // Use 'qty' not 'quantity'
22897          variant_id: 0, // Default variant
22898          meta: {
22899            name: activeProductData.name,
22900            image_url: activeProductData.image
22901          }
22902        }),
22903        credentials: 'same-origin'
22904      });
22905      
22906      const result = await response.json();
22907      
22908      // Check both response status and result.ok
22909      if (!response.ok || !result.ok) {
22910        throw new Error(result.error || result.message || 'Gagal menambahkan ke keranjang');
22911      }
22912      
22913      // Success animation
22914      button.classList.remove('loading');
22915      button.classList.add('success');
22916      
22917      // Update cart badge
22918      updateCartBadge();
22919
22920      // PlazaAnalytics tracking
22921      if (window.PlazaAnalytics) {
22922        const _slug = (activeProductData.url || '').replace(/.*\/product\//, '');
22923        window.PlazaAnalytics.track('add_to_cart', { slug: _slug });
22924      }
22925
22926      // Show success toast
22927      showToast('Produk ditambahkan ke keranjang', 'success');
22928      
22929      // Google Analytics
22930      if (typeof gtag === 'function') {
22931        gtag('event', 'add_to_cart', {
22932          event_category: 'Ecommerce',
22933          event_label: 'Quick Add to Cart',
22934          items: [{
22935            item_id: activeProductData.id,
22936            item_name: activeProductData.name,
22937            price: activeProductData.price,
22938            quantity: 1
22939          }]
22940        });
22941      }
22942      
22943      // Close sheet after short delay
22944      setTimeout(() => {
22945        closeBottomSheet();
22946        button.classList.remove('success');
22947        button.disabled = false;
22948      }, 1000);
22949      
22950    } catch (error) {
22951      console.error('Add to cart error:', error);
22952      button.classList.remove('loading');
22953      button.disabled = false;
22954      showToast(error.message || 'Terjadi kesalahan', 'error');
22955    }
22956  }
22957  
22958  /**
22959   * Handle add to compare
22960   */
22961  async function handleAddToCompare() {
22962    if (!activeProductData) return;
22963    
22964    const button = document.getElementById('pca-add-compare');
22965    if (!button) return;
22966    
22967    // Check if already in compare
22968    const isActive = button.classList.contains('active');
22969    
22970    // Check 5-product limit before adding
22971    if (!isActive) {
22972      const mainBadge = document.getElementById('compareCount');
22973      const currentCount = mainBadge ? parseInt(mainBadge.textContent) || 0 : 0;
22974      
22975      if (currentCount >= 5) {
22976        showToast('Maksimal 5 produk untuk perbandingan', 'warning');
22977        return;
22978      }
22979    }
22980    
22981    // Show loading state
22982    button.classList.add('loading');
22983    button.disabled = true;
22984    
22985    try {
22986      if (isActive) {
22987        // Remove from compare
22988        await removeFromCompare(activeProductData.id);
22989        showToast('Produk dihapus dari perbandingan', 'success');
22990      } else {
22991        // Add to compare
22992        await addToCompare(activeProductData.id);
22993        showToast('Produk ditambahkan ke perbandingan', 'success');
22994        
22995        // Trigger fly animation
22996        triggerFlyToCompareAnimation();
22997      }
22998      
22999      // Success animation
23000      button.classList.remove('loading');
23001      button.classList.add('success');
23002      
23003      // Update compare button state
23004      updateCompareButtonState(activeProductData.id);
23005      
23006      // Close sheet after short delay
23007      setTimeout(() => {
23008        closeBottomSheet();
23009        button.classList.remove('success');
23010        button.disabled = false;
23011      }, 800);
23012      
23013    } catch (error) {
23014      console.error('Compare error:', error);
23015      button.classList.remove('loading');
23016      button.disabled = false;
23017      showToast(error.message || 'Terjadi kesalahan', 'error');
23018    }
23019  }
23020  
23021  /**
23022   * Add to compare (calls existing API)
23023   */
23024  async function addToCompare(productId) {
23025    const response = await fetch('/api/compare/add.php', {
23026      method: 'POST',
23027      headers: {
23028        'Content-Type': 'application/json',
23029      },
23030      body: JSON.stringify({
23031        product_id: productId,
23032        variant_id: 0
23033      }),
23034      credentials: 'same-origin'
23035    });
23036    
23037    const result = await response.json();
23038    
23039    if (!result.success) {
23040      throw new Error(result.error || 'Gagal menambahkan produk');
23041    }
23042    
23043    // Trigger compare list reload if function exists
23044    if (typeof window.productCompare !== 'undefined' && typeof window.productCompare.loadCompareList === 'function') {
23045      await window.productCompare.loadCompareList();
23046    }
23047    
23048    return result;
23049  }
23050  
23051  /**
23052   * Remove from compare (calls existing API)
23053   */
23054  async function removeFromCompare(productId) {
23055    const response = await fetch('/api/compare/remove.php', {
23056      method: 'POST',
23057      headers: {
23058        'Content-Type': 'application/json',
23059      },
23060      body: JSON.stringify({
23061        product_id: productId
23062      }),
23063      credentials: 'same-origin'
23064    });
23065    
23066    const result = await response.json();
23067    
23068    if (!result.success) {
23069      throw new Error(result.error || 'Gagal menghapus produk');
23070    }
23071    
23072    // Trigger compare list reload if function exists
23073    if (typeof window.productCompare !== 'undefined' && typeof window.productCompare.loadCompareList === 'function') {
23074      await window.productCompare.loadCompareList();
23075    }
23076    
23077    return result;
23078  }
23079  
23080  /**
23081   * Update cart badge
23082   */
23083  function updateCartBadge() {
23084    // Try to call global cart update function if exists
23085    if (typeof window.updateCartBadge === 'function') {
23086      window.updateCartBadge();
23087    } else {
23088      // Fallback: increment badge manually
23089      const badge = document.getElementById('cartCount');
23090      if (badge) {
23091        const currentCount = parseInt(badge.textContent) || 0;
23092        badge.textContent = currentCount + 1;
23093        badge.classList.add('active');
23094      }
23095    }
23096  }
23097  
23098  /**
23099   * Trigger fly to compare animation
23100   */
23101  function triggerFlyToCompareAnimation() {
23102    const compareBtn = document.getElementById('compareBtn');
23103    if (!compareBtn || !activeProductData) return;
23104    
23105    // Create flying element
23106    const flyingElement = document.createElement('div');
23107    flyingElement.className = 'flying-product';
23108    flyingElement.style.cssText = `
23109      position: fixed;
23110      width: 60px;
23111      height: 60px;
23112      z-index: 99999;
23113      pointer-events: none;
23114    `;
23115    
23116    flyingElement.innerHTML = `
23117      <img src="${activeProductData.image}" 
23118           style="width:100%;height:100%;object-fit:cover;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.3);">
23119    `;
23120    
23121    // Position at bottom sheet
23122    const sheetRect = bottomSheet.getBoundingClientRect();
23123    const targetRect = compareBtn.getBoundingClientRect();
23124    
23125    flyingElement.style.top = `${sheetRect.top + 20}px`;
23126    flyingElement.style.left = `${sheetRect.left + 20}px`;
23127    
23128    document.body.appendChild(flyingElement);
23129    
23130    // Animate to target
23131    requestAnimationFrame(() => {
23132      flyingElement.style.transition = 'all 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94)';
23133      flyingElement.style.top = `${targetRect.top + 10}px`;
23134      flyingElement.style.left = `${targetRect.left + 10}px`;
23135      flyingElement.style.transform = 'scale(0.3)';
23136      flyingElement.style.opacity = '0';
23137    });
23138    
23139    // Clean up
23140    setTimeout(() => {
23141      if (flyingElement.parentNode) {
23142        flyingElement.parentNode.removeChild(flyingElement);
23143      }
23144    }, 800);
23145  }
23146  
23147  /**
23148   * Format price as Rupiah
23149   */
23150  function formatPrice(price) {
23151    if (!price || price === 0) return 'Rp 0';
23152    return 'Rp ' + parseInt(price).toLocaleString('id-ID');
23153  }
23154  
23155  /**
23156   * Escape HTML
23157   */
23158  function escapeHtml(text) {
23159    const div = document.createElement('div');
23160    div.textContent = text;
23161    return div.innerHTML;
23162  }
23163  
23164  /**
23165   * Show toast notification (uses global showToast if available)
23166   */
23167  function showToast(message, type = 'info') {
23168    if (typeof window.showToast === 'function') {
23169      window.showToast({
23170        title: type === 'success' ? 'Berhasil' : type === 'error' ? 'Gagal' : 'Info',
23171        message: message,
23172        type: type,
23173        duration: 3000
23174      });
23175    } else {
23176      // Fallback: console log
23177      console.log(`[${type.toUpperCase()}] ${message}`);
23178    }
23179  }
23180  
23181  /**
23182   * Expose functions for external access
23183   */
23184  window.ProductCardActions = {
23185    openSheet: openBottomSheet,
23186    closeSheet: closeBottomSheet,
23187    updateCompareState: updateCompareButtonState
23188  };
23189  
23190})();
23191
23192</script>
23192
23193    
23193<script data-inlined="product-reviews.js">
23194/**
23195 * Product Reviews - Frontend JavaScript
23196 * Handles review display, submission, and helpful voting
23197 */
23198
23199const ProductReviews = (function() {
23200  'use strict';
23201
23202  let currentPage = 1;
23203  let currentSort = 'helpful';
23204  let productId = null;
23205  let isLoading = false;
23206
23207  /**
23208   * Public API: Initialize reviews for a product
23209   */
23210  function init(pid) {
23211    productId = pid;
23212    
23213    if (!productId) {
23214      console.warn('Product ID not provided to ProductReviews.init()');
23215      return;
23216    }
23217
23218    initializeReviewTab();
23219    loadReviews();
23220  }
23221
23222  /**
23223   * Initialize review tab functionality
23224   */
23225  function initializeReviewTab() {
23226    // Tab switching
23227    const tabButtons = document.querySelectorAll('.tab-btn');
23228    tabButtons.forEach(btn => {
23229      btn.addEventListener('click', function() {
23230        const tabName = this.dataset.tab;
23231        switchTab(tabName);
23232      });
23233    });
23234
23235    // Sort dropdown
23236    const sortSelect = document.getElementById('review-sort');
23237    if (sortSelect) {
23238      sortSelect.addEventListener('change', function() {
23239        currentSort = this.value;
23240        currentPage = 1;
23241        loadReviews();
23242      });
23243    }
23244
23245    // Load more button
23246    const loadMoreBtn = document.getElementById('load-more-reviews');
23247    if (loadMoreBtn) {
23248      loadMoreBtn.addEventListener('click', function() {
23249        currentPage++;
23250        loadReviews(true); // Append mode
23251      });
23252    }
23253
23254    // Write review button
23255    const writeReviewBtn = document.getElementById('write-review-btn');
23256    if (writeReviewBtn) {
23257      writeReviewBtn.addEventListener('click', showReviewModal);
23258    }
23259  }
23260
23261  /**
23262   * Switch between product tabs
23263   */
23264  function switchTab(tabName) {
23265    // Update button states
23266    document.querySelectorAll('.tab-btn').forEach(btn => {
23267      btn.classList.toggle('active', btn.dataset.tab === tabName);
23268    });
23269
23270    // Update content visibility
23271    document.querySelectorAll('.tab-content').forEach(content => {
23272      content.style.display = content.id === `${tabName}-tab` ? 'block' : 'none';
23273    });
23274  }
23275
23276  /**
23277   * Load reviews from API
23278   */
23279  async function loadReviews(append = false) {
23280    if (isLoading) return;
23281    isLoading = true;
23282
23283    const container = document.getElementById('reviews-list');
23284    const loadMoreBtn = document.getElementById('load-more-reviews');
23285
23286    // Check if review section exists (product might not have reviews section)
23287    if (!container) {
23288      console.log('Review section not found - product may not have reviews yet');
23289      isLoading = false;
23290      return;
23291    }
23292
23293    try {
23294      if (!append) {
23295        container.innerHTML = '<div class="review-loading">Memuat ulasan...</div>';
23296      } else {
23297        loadMoreBtn.disabled = true;
23298        loadMoreBtn.textContent = 'Memuat...';
23299      }
23300
23301      const response = await fetch(`/api/reviews/list.php?product_id=${productId}&sort=${currentSort}&page=${currentPage}&limit=10`);
23302      const data = await response.json();
23303
23304      if (!data.success) {
23305        throw new Error(data.message || 'Gagal memuat ulasan');
23306      }
23307
23308      // Update statistics (only on initial load)
23309      if (!append && data.stats) {
23310        updateReviewStats(data.stats);
23311      }
23312
23313      // Render reviews
23314      if (!append) {
23315        container.innerHTML = '';
23316      }
23317
23318      const reviews = data.reviews || [];
23319      
23320      if (reviews.length === 0 && !append) {
23321        container.innerHTML = '<div class="no-reviews">Belum ada ulasan untuk produk ini. Jadilah yang pertama!</div>';
23322      } else {
23323        reviews.forEach(review => {
23324          container.appendChild(createReviewElement(review));
23325        });
23326      }
23327
23328      // Update load more button
23329      if (loadMoreBtn) {
23330        if (data.pagination.has_more) {
23331          loadMoreBtn.style.display = 'block';
23332          loadMoreBtn.disabled = false;
23333          loadMoreBtn.textContent = 'Muat Lebih Banyak';
23334        } else {
23335          loadMoreBtn.style.display = 'none';
23336        }
23337      }
23338
23339    } catch (error) {
23340      console.error('Load reviews error:', error);
23341      container.innerHTML = '<div class="review-error">Gagal memuat ulasan. Silakan coba lagi.</div>';
23342    } finally {
23343      isLoading = false;
23344    }
23345  }
23346
23347  /**
23348   * Update review statistics display
23349   */
23350  function updateReviewStats(stats) {
23351    const avgRatingEl = document.getElementById('avg-rating');
23352    const totalReviewsEl = document.getElementById('total-reviews');
23353    const ratingBarsContainer = document.getElementById('rating-bars');
23354
23355    if (avgRatingEl) {
23356      avgRatingEl.textContent = stats.average_rating.toFixed(1);
23357    }
23358
23359    if (totalReviewsEl) {
23360      totalReviewsEl.textContent = `${stats.total_reviews} ulasan`;
23361    }
23362
23363    // Update rating breakdown bars
23364    if (ratingBarsContainer) {
23365      ratingBarsContainer.innerHTML = '';
23366      stats.rating_breakdown.forEach(item => {
23367        const bar = document.createElement('div');
23368        bar.className = 'rating-bar';
23369        bar.innerHTML = `
23370          <span class="rating-label">${item.rating}★</span>
23371          <div class="bar-container">
23372            <div class="bar-fill" style="width: ${item.percentage}%"></div>
23373          </div>
23374          <span class="rating-count">${item.count}</span>
23375        `;
23376        ratingBarsContainer.appendChild(bar);
23377      });
23378    }
23379
23380    // Update tab count
23381    const reviewTab = document.querySelector('[data-tab="reviews"]');
23382    if (reviewTab && stats.total_reviews > 0) {
23383      reviewTab.textContent = `Ulasan (${stats.total_reviews})`;
23384    }
23385  }
23386
23387  /**
23388   * Create review element
23389   */
23390  function createReviewElement(review) {
23391    const div = document.createElement('div');
23392    div.className = 'review-item';
23393    div.dataset.reviewId = review.ID;
23394
23395    // Build stars
23396    const stars = '★'.repeat(review.RATING) + '☆'.repeat(5 - review.RATING);
23397
23398    // Build images if available
23399    let imagesHtml = '';
23400    if (review.images && review.images.length > 0) {
23401      imagesHtml = '<div class="review-images">';
23402      review.images.forEach(img => {
23403        imagesHtml += `<img src="${escapeHtml(img)}" alt="Review image" class="review-img" loading="lazy">`;
23404      });
23405      imagesHtml += '</div>';
23406    }
23407
23408    // Verified purchase badge
23409    const verifiedBadge = review.IS_VERIFIED_PURCHASE === 'Y' 
23410      ? '<span class="verified-badge">✓ Pembelian Terverifikasi</span>' 
23411      : '';
23412    
23413    // Use DISPLAY_NAME if available (for anonymous reviews), otherwise use FULL_NAME
23414    const displayName = review.DISPLAY_NAME || review.FULL_NAME || 'Anonymous';
23415    const anonymousIndicator = review.IS_ANONYMOUS === 'Y' 
23416      ? '<span class="anonymous-badge" title="Nama pengguna disamark
23416an untuk privasi">🔒</span>' 
23417      : '';
23418
23419    div.innerHTML = `
23420      <div class="review-header">
23421        <div class="review-author">
23422          <strong>${escapeHtml(displayName)}</strong>
23423          ${anonymousIndicator}
23424          ${verifiedBadge}
23425        </div>
23426        <div class="review-rating">
23427          <span class="stars">${stars}</span>
23428        </div>
23429      </div>
23430      <div class="review-date">${review.created_ago}</div>
23431      <div class="review-text">${escapeHtml(review.REVIEW_TEXT).replace(/\n/g, '<br>')}</div>
23432      ${imagesHtml}
23433      <div class="review-actions">
23434        <button class="btn-helpful" data-review-id="${review.ID}">
23435          <span class="helpful-icon">👍</span>
23436          <span class="helpful-text">Membantu (${review.HELPFUL_COUNT || 0})</span>
23437        </button>
23438      </div>
23439    `;
23440
23441    // Add helpful button listener
23442    const helpfulBtn = div.querySelector('.btn-helpful');
23443    helpfulBtn.addEventListener('click', () => markAsHelpful(review.ID));
23444
23445    return div;
23446  }
23447
23448  /**
23449   * Get CSRF token from meta tag
23450   */
23451  function getCsrfToken() {
23452    const metaTag = document.querySelector('meta[name="csrf-token"]');
23453    return metaTag ? metaTag.getAttribute('content') : '';
23454  }
23455
23456  /**
23457   * Mark review as helpful
23458   */
23459  async function markAsHelpful(reviewId) {
23460    // Check if logged in
23461    const isLoggedIn = document.body.classList.contains('logged-in') || 
23462                      document.querySelector('meta[name="user-logged-in"]')?.content === 'true';
23463
23464    if (!isLoggedIn) {
23465      showModal({
23466        title: 'Login Diperlukan',
23467        message: 'Silakan login terlebih dahulu untuk memberikan vote pada ulasan ini.',
23468        icon: 'info',
23469        buttons: [
23470          { text: 'Batal', className: 'modal-btn-secondary' },
23471          { text: 'Login', className: 'modal-btn-primary', callback: () => window.location.href = '/login' }
23472        ]
23473      });
23474      return;
23475    }
23476
23477    // Get current button element
23478    const reviewItem = document.querySelector(`[data-review-id="${reviewId}"]`);
23479    const helpfulBtn = reviewItem?.querySelector('.btn-helpful');
23480    
23481    if (!helpfulBtn) return;
23482    
23483    // Disable button during request
23484    helpfulBtn.disabled = true;
23485    const originalText = helpfulBtn.querySelector('.helpful-text').textContent;
23486
23487    try {
23488      const response = await fetch('/api/reviews/helpful.php', {
23489        method: 'POST',
23490        headers: { 
23491          'Content-Type': 'application/json',
23492          'X-CSRF-Token': getCsrfToken()
23493        },
23494        credentials: 'same-origin',
23495        body: JSON.stringify({ 
23496          csrf_token: getCsrfToken(),
23497          review_id: reviewId 
23498        })
23499      });
23500
23501      const data = await response.json();
23502
23503      if (data.success) {
23504        // Update button count immediately based on action
23505        const currentCount = parseInt(originalText.match(/\d+/)?.[0] || 0);
23506        const newCount = data.action === 'added' ? currentCount + 1 : currentCount - 1;
23507        helpfulBtn.querySelector('.helpful-text').textContent = `Membantu (${newCount})`;
23508        
23509        // Show success message
23510        showToast(data.message, 'success');
23511        
23512        // Re-enable button
23513        helpfulBtn.disabled = false;
23514      } else {
23515        showToast(data.message, 'error');
23516        helpfulBtn.disabled = false;
23517      }
23518
23519    } catch (error) {
23520      console.error('Mark helpful error:', error);
23521      showToast('Gagal memberikan vote. Silakan coba lagi.', 'error');
23522      helpfulBtn.disabled = false;
23523    }
23524  }
23525
23526  /**
23527   * Show review submission modal (placeholder)
23528   */
23529  function showReviewModal() {
23530    // TODO: Implement review modal
23531    // For now, redirect to account page
23532    window.location.href = '/account/orders';
23533  }
23534
23535  /**
23536   * Show toast notification
23537   */
23538  function showToast(message, type = 'info') {
23539    const toast = document.createElement('div');
23540    toast.className = `review-toast toast-${type}`;
23541    toast.textContent = message;
23542    
23543    document.body.appendChild(toast);
23544    
23545    setTimeout(() => {
23546      toast.classList.add('show');
23547    }, 100);
23548    
23549    setTimeout(() => {
23550      toast.classList.remove('show');
23551      setTimeout(() => toast.remove(), 300);
23552    }, 3000);
23553  }
23554
23555  /**
23556   * Show modal dialog
23557   * @param {Object} options - Modal options
23558   * @param {string} options.title - Modal title
23559   * @param {string} options.message - Modal message
23560   * @param {string} options.icon - Icon type (info, success, warning, danger)
23561   * @param {Array} options.buttons - Array of button objects {text, className, callback}
23562   */
23563  function showModal(options) {
23564    const {
23565      title = 'Konfirmasi',
23566      message = '',
23567      icon = 'info',
23568      buttons = [{ text: 'OK', className: 'modal-btn-primary' }]
23569    } = options;
23570
23571    // Icon emoji mapping
23572    const iconEmojis = {
23573      info: 'ℹ️',
23574      success: '✓',
23575      warning: '⚠️',
23576      danger: '✕'
23577    };
23578
23579    // Create modal HTML
23580    const modalHTML = `
23581      <div class="modal-overlay" id="helpful-vote-modal" style="display: flex !important; align-items: center !important; justify-content: center !important;">
23582        <div class="modal-dialog">
23583          <div class="modal-header">
23584            <h3 class="modal-title">
23585              <span class="modal-icon ${icon}">${iconEmojis[icon] || iconEmojis.info}</span>
23586              ${escapeHtml(title)}
23587            </h3>
23588          </div>
23589          <div class="modal-body">
23590            <p class="modal-message">${escapeHtml(message)}</p>
23591            <div class="modal-actions">
23592              ${buttons.map((btn, index) => 
23593                `<button class="modal-btn ${btn.className || 'modal-btn-secondary'}" data-index="${index}">${escapeHtml(btn.text)}</button>`
23594              ).join('')}
23595            </div>
23596          </div>
23597        </div>
23598      </div>
23599    `;
23600
23601    // Append to body
23602    const tempDiv = document.createElement('div');
23603    tempDiv.innerHTML = modalHTML;
23604    const modal = tempDiv.firstElementChild;
23605    document.body.appendChild(modal);
23606
23607    // Add event listeners to buttons
23608    buttons.forEach((btn, index) => {
23609      const btnElement = modal.querySelector(`[data-index="${index}"]`);
23610      btnElement.addEventListener('click', () => {
23611        closeModal(modal);
23612        if (btn.callback && typeof btn.callback === 'function') {
23613          btn.callback();
23614        }
23615      });
23616    });
23617
23618    // Close on overlay click
23619    modal.addEventListener('click', (e) => {
23620      if (e.target === modal) {
23621        closeModal(modal);
23622      }
23623    });
23624
23625    // Show modal with animation
23626    setTimeout(() => modal.classList.add('show'), 10);
23627  }
23628
23629  /**
23630   * Close modal dialog
23631   */
23632  function closeModal(modal) {
23633    modal.classList.remove('show');
23634    setTimeout(() => modal.remove(), 200);
23635  }
23636
23637  /**
23638   * Escape HTML for XSS protection
23639   */
23640  function escapeHtml(text) {
23641    const div = document.createElement('div');
23642    div.textContent = text;
23643    return div.innerHTML;
23644  }
23645
23646  /**
23647   * Get product ID from URL
23648   */
23649  function getProductIdFromUrl() {
23650    const match = window.location.pathname.match(/\/product\/(\d+)/);
23651    return match ? match[1] : null;
23652  }
23653
23654  // Public API
23655  return {
23656    init: init
23657  };
23658
23659})();
23660
23661</script>
23661
23662    
23662<script data-inlined="review-submission.js">
23663/**
23664 * Review Submission Modal & Handler
23665 * Handles review form display, validation, and submission
23666 */
23667
23668(function() {
23669    'use strict';
23670
23671    let currentProductId = null;
23672    let currentOrderId = null;
23673    let currentSoRefId = null;
23674    let selectedRating = 0;
23675    let uploadedImages = [];
23676
23677    // Get CSRF token from meta tag
23678    function getCsrfToken() {
23679        const metaTag = document.querySelector('meta[name="csrf-token"]');
23680        return metaTag ? metaTag.getAttribute('content') : '';
23681    }
23682
23683    // Initialize
23684    function init() {
23685        createReviewModal();
23686        attachEventListeners();
23687        loadReviewableOrders();
23688    }
23689
23690    // Create review modal HTML
23691    function createReviewModal() {
23692        const modalHTML = `
23693            <div class="modal-overlay" id="review-modal-overlay" style="display:none;">
23694                <div class="modal-content review-modal">
23695                    <div class="modal-header">
23696                        <h3 class="modal-title">Tulis Ulasan</h3>
23697                        <button type="button" class="modal-close" id="review-modal-close" aria-label="Tutup">
23698                            <svg width="20" height="20" viewBox="0 0 24 24">
23699                                <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
23700                            </svg>
23701                        </button>
23702                    </div>
23703                    
23704                    <div class="modal-body">
23705                        <form id="review-form" class="review-form">
23706                            <!-- Product Info -->
23707                            <div class="review-product-info" id="review-product-info">
23708                                <img id="review-product-image" src="" alt="" class="review-product-thumb">
23709                                <div class="review-product-details">
23710                                    <h4 id="review-product-name"></h4>
23711                                    <p id="review-product-sku"></p>
23712                                </div>
23713                            </div>
23714
23715                            <!-- Rating -->
23716                            <div class="form-group">
23717                                <label class="form-label">Rating <span style="color:#ef4444;">*</span></label>
23718                                <div class="star-rating-input" id="star-rating-input">
23719                                    <button type="button" class="star-btn" data-rating="1">★</button>
23720                                    <button type="button" class="star-btn" data-rating="2">★</button>
23721                                    <button type="button" class="star-btn" data-rating="3">★</button>
23722                                    <button type="button" class="star-btn" data-rating="4">★</button>
23723                                    <button type="button" class="star-btn" data-rating="5">★</button>
23724                                </div>
23725                                <p class="rating-label" id="rating-label">Pilih rating Anda</p>
23726                            </div>
23727
23728                            <!-- Review Text -->
23729                            <div class="form-group">
23730                                <label class="form-label" for="review-text">Ulasan Anda <span style="color:#ef4444;">*</span></label>
23731                                <textarea 
23732                                    id="review-text" 
23733                                    name="review_text"
23734                                    class="form-control" 
23735                                    rows="6" 
23736                                    placeholder="Ceritakan pengalaman Anda dengan produk ini..."
23737                                    minlength="20"
23738                                    maxlength="5000"
23739                                    required></textarea>
23740                                <p class="form-hint">
23741                                    <span id="review-char-count">0</span>/5000 karakter
23742                                    <span style="color:#059669;font-weight:500;">
23743                                        💰 +1,000 point untuk ulasan ≥ 100 karakter
23744                                    </span>
23745                                </p>
23746                            </div>
23747
23748                            <!-- Image Upload -->
23749                            <div class="form-group">
23750                                <label class="form-label">Upload Foto (Opsional)</label>
23751                                <div class="image-upload-container">
23752                                    <input type="file" 
23753                                           id="review-images" 
23754                                           name="images[]" 
23755                                           accept="image/jpeg,image/jpg,image/png,image/webp"
23756                                           multiple
23757                                           style="display:none;">
23758                                    <button type="button" class="btn-upload-trigger" id="upload-trigger">
23759                                        <svg width="24" height="24" viewBox="0 0 24 24">
23760                                            <path fill="currentColor" d="M19 7v2.99s-1.99.01-2 0V7h-3s.01-1.99 0-2h3V2h2v3h3v2h-3zm-3 4V8h-3V5H5c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-8h-3zM5 19l3-4 2 3 3-4 4 5H5z"/>
23761                                        </svg>
23762                                        <span>Pilih Foto</span>
23763                                    </button>
23764                                    <p class="form-hint">
23765                                        Max 5 foto, format JPG/PNG/WebP, max 2MB per foto
23766                                        <span style="color:#059669;font-weight:500;">
23767                                            💰 +2,000 point untuk ulasan dengan foto
23768                                        </span>
23769                                    </p>
23770                                </div>
23771                                <div class="image-preview-grid" id="image-preview-grid"></div>
23772                            </div>
23773
23774                            <!-- Point Reward Info -->
23775                            <div class="point-reward-info">
23776                                <div class="pri-header">
23777                                    <span>🎁</span>
23778                                    <span>Dapatkan Point Reward</span>
23779                                </div>
23780                                <ul class="pri-list">
23781                                    <li>✅ Ulasan dasar: <strong>5,000 point</strong></li>
23782                                    <li id="pri-long-text" style="opacity:0.5;">📝 Ulasan panjang (≥100 kata): <strong>+1,000 point</strong></li>
23783                                    <li id="pri-images" style="opacity:0.5;">📸 Dengan foto: <strong>+2,000 point</strong></li>
23784                                </ul>
23785                                <div class="pri-total">
23786                                    Total Reward: <strong id="pri-total-points">5,000 point</strong>
23787                                </div>
23788                            </div>
23789
23790                            <!-- Privacy Option -->
23791                            <div class="form-group" style="margin-top:20px;padding:15px;background:#f8fafc;border-radius:8px;">
23792                                <label class="checkbox-label" style="display:flex;align-items:center;cursor:pointer;margin:0;">
23793                                    <input type="checkbox" 
23794                                           id="review-anonymous" 
23795                                           name="is_anonymous"
23796                                           style="margin-right:8px;width:18px;height:18px;cursor:pointer;">
23797                                    <span style="font-size:14px;">
23798                                        <strong>🔒 Sembunyikan nama saya</strong>
23799                                        <span style="color:#64748b;display:block;font-size:13px;margin-top:4px;" id="review-anonymous-preview">
23800                                            Nama Anda akan ditampilkan dengan disamarkan untuk menjaga privasi
23801                                        </span>
23802                                    </span>
23803                                </label>
23804                            </div>
23805
23806                            <!-- Error Alert -->
23807                            <div class="form-error" id="review-form-error" style="display:none;"></div>
23808
23809                            <!-- Submit Buttons -->
23810                            <div class="modal-footer">
23811                                <button type="button" class="btn btn-secondary" id="review-cancel">Batal</button>
23812                                <button type="submit" class="btn btn-primary" id="review-submit">
23813                                    Kirim Ulasan
23814                                </button>
23815                            </div>
23816                        </form>
23817                    </div>
23818                </div>
23819            </div>
23820        `;
23821
23822        document.body.insertAdjacentHTML('beforeend', modalHTML);
23823    }
23824
23825    // Attach event listeners
23826    function attachEventListeners() {
23827        // Modal triggers - event delegation
23828        document.addEventListener('click', function(e) {
23829            const btn = e.target.closest('.btn-write-review');
23830            if (btn) {
23831                e.preventDefault();
23832                openReviewModal(
23833                    btn.dataset.productId,
23834                    btn.dataset.orderId,
23835                    btn.dataset.productName,
23836                    btn.dataset.productImage,
23837                    btn.dataset.productSku,
23838                    btn.dataset.soRefId || null
23839                );
23840            }
23841        });
23842
23843        // Close modal
23844        setTimeout(() => {
23845            const closeBtn = document.getElementById('review-modal-close');
23846            const cancelBtn = document.getElementById('review-cancel');
23847            const overlay = document.getElementById('review-modal-overlay');
23848
23849            if (closeBtn) {
23850                closeBtn.addEventListener('click', closeReviewModal);
23851            }
23852            if (cancelBtn) {
23853                cancelBtn.addEventListener('click', closeReviewModal);
23854            }
23855            if (overlay) {
23856                overlay.addEventListener('click', function(e) {
23857                    if (e.target === overlay) closeReviewModal();
23858                });
23859            }
23860        }, 100);
23861
23862        // Star rating
23863        setTimeout(() => {
23864            const starButtons = document.querySelectorAll('.star-btn');
23865            starButtons.forEach(btn => {
23866                btn.addEventListener('click', function(e) {
23867                    e.preventDefault();
23868                    const rating = parseInt(this.dataset.rating);
23869                    setRating(rating);
23870                });
23871            });
23872        }, 100);
23873
23874        // Character count
23875        setTimeout(() => {
23876            const textarea = document.getElementById('review-text');
23877            if (textarea) {
23878                textarea.addEventListener('input', function() {
23879                    updateCharCount();
23880                    calculatePotentialPoints();
23881                });
23882            }
23883        }, 100);
23884
23885        // Image upload
23886        setTimeout(() => {
23887            const uploadTrigger = document.getElementById('upload-trigger');
23888            const fileInput = document.getElementById('review-images');
23889            
23890            if (uploadTrigger && fileInput) {
23891                uploadTrigger.addEventListener('click', () => fileInput.click());
23892                fileInput.addEventListener('change', handleImageSelection);
23893            }
23894        }, 100);
23895
23896        // Form submit
23897        setTimeout(() => {
23898            const form = document.getElementById('review-form');
23899            if (form) {
23900                form.addEventListener('submit', handleSubmit);
23901            }
23902        }, 100);
23903    }
23904
23905    // Open review modal
23906    function openReviewModal(productId, orderId, productName, productImage, productSku, soRefId = null) {
23907        const overlay = document.getElementById('review-modal-overlay');
23908        if (!overlay) {
23909            console.error('Modal overlay not found!');
23910            return;
23911        }
23912        
23913        currentProductId = productId;
23914        currentOrderId = orderId;
23915        currentSoRefId = soRefId;
23916        selectedRating = 0;
23917        uploadedImages = [];
23918
23919        // Set product info
23920        const nameEl = document.getElementById('review-product-name');
23921        const imageEl = document.getElementById('review-product-image');
23922        const skuEl = document.getElementById('review-product-sku');
23923        
23924        if (nameEl) nameEl.textContent = productName;
23925        if (imageEl) imageEl.src = productImage || '/assets/img/no-image.png';
23926        if (skuEl) skuEl.textContent = 'SKU: ' + productSku;
23927
23928        // Update privacy preview with actual user name
23929        updatePrivacyPreview();
23930
23931        // Reset form
23932        const form = document.getElementById('review-form');
23933        const previewGrid = document.getElementById('image-preview-grid');
23934        const errorEl = document.getElementById('review-form-error');
23935        
23936        if (form) form.reset();
23937        if (previewGrid) previewGrid.innerHTML = '';
23938        if (errorEl) errorEl.style.display = 'none';
23939        
23940        setRating(0);
23941        updateCharCount();
23942        calculatePotentialPoints();
23943
23944        // Show modal
23945        overlay.style.display = 'flex';
23946        overlay.classList.add('show');
23947        document.body.style.overflow = 'hidden';
23948    }
23949
23950    // Close review modal
23951    function closeReviewModal() {
23952        const overlay = document.getElementById('review-modal-overlay');
23953        if (overlay) {
23954            overlay.classList.remove('show');
23955            setTimeout(() => {
23956                overlay.style.display = 'none';
23957            }, 200); // Wait for fade animation
23958        }
23959        document.body.style.overflow = '';
23960    }
23961
23962    // Set star rating
23963    function setRating(rating) {
23964        selectedRating = rating;
23965        const stars = document.querySelectorAll('.star-btn');
23966        const labels = ['', 'Sangat Buruk', 'Buruk', 'Cukup', 'Bagus', 'Sangat Bagus'];
23967        
23968        stars.forEach((star, index) => {
23969            if (index < rating) {
23970                star.classList.add('active');
23971            } else {
23972                star.classList.remove('active');
23973            }
23974        });
23975
23976        document.getElementById('rating-label').textContent = labels[rating] || 'Pilih rating Anda';
23977    }
23978
23979    // Update character count
23980    function updateCharCount() {
23981        const textarea = document.getElementById('review-text');
23982        const count = textarea.value.length;
23983        document.getElementById('review-char-count').textContent = count;
23984    }
23985
23986    // Calculate potential points
23987    function calculatePotentialPoints() {
23988        const textarea = document.getElementById('review-text');
23989        if (!textarea) return;
23990        
23991        const charCount = textarea.value.trim().length;
23992        const hasImages = uploadedImages.length > 0;
23993
23994        let totalPoints = 5000;
23995        let longTextActive = charCount >= 100;
23996        let imagesActive = hasImages;
23997
23998        if (longTextActive) totalPoints += 1000;
23999        if (imagesActive) totalPoints += 2000;
24000
24001        // Update UI
24002        const longTextEl = document.getElementById('pri-long-text');
24003        const imagesEl = document.getElementById('pri-images');
24004        const totalEl = document.getElementById('pri-total-points');
24005        
24006        if (longTextEl) longTextEl.style.opacity = longTextActive ? '1' : '0.5';
24007        if (imagesEl) imagesEl.style.opacity = imagesActive ? '1' : '0.5';
24008        if (totalEl) totalEl.textContent = totalPoints.toLocaleString('id-ID') + ' point';
24009    }
24010
24011    // Handle image selection
24012    function handleImageSelection(e) {
24013        const files = Array.from(e.target.files);
24014        
24015        if (files.length > 5) {
24016            showError('Maksimal 5 foto yang dapat diupload');
24017            e.target.value = ''; // Reset input
24018            return;
24019        }
24020
24021        uploadedImages = [];
24022        const previewGrid = document.getElementById('image-preview-grid');
24023        previewGrid.innerHTML = '';
24024
24025        files.forEach((file, index) => {
24026            // Validate file type
24027            if (!file.type.match(/image\/(jpeg|jpg|png|webp)/)) {
24028                showError('Format file harus JPG, PNG, atau WebP');
24029                return;
24030            }
24031
24032            // Validate file size (2MB)
24033            if (file.size > 2 * 1024 * 1024) {
24034                showError('Ukuran file maksimal 2MB');
24035                return;
24036            }
24037
24038            uploadedImages.push(file);
24039
24040            // Create preview
24041            const reader = new FileReader();
24042            reader.onload = function(e) {
24043                const previewHTML = `
24044                    <div class="image-preview-item">
24045                        <img src="${e.target.result}" alt="Preview ${index + 1}">
24046                        <button type="button" class="remove-image" data-index="${index}" aria-label="Hapus">
24047                            <svg width="16" height="16" viewBox="0 0 24 24">
24048                                <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
24049                            </svg>
24050                        </button>
24051                    </div>
24052                `;
24053                previewGrid.insertAdjacentHTML('beforeend', previewHTML);
24054            };
24055            reader.readAsDataURL(file);
24056        });
24057
24058        calculatePotentialPoints();
24059
24060        // Attach remove handlers
24061        setTimeout(() => {
24062            document.querySelectorAll('.remove-image').forEach(btn => {
24063                btn.addEventListener('click', function() {
24064                    const index = parseInt(this.dataset.index);
24065                    removeImage(index);
24066                });
24067            });
24068        }, 100);
24069    }
24070
24071    // Remove image
24072    function removeImage(index) {
24073        uploadedImages.splice(index, 1);
24074        
24075        const previewGrid = document.getElementById('image-preview-grid');
24076        previewGrid.innerHTML = '';
24077
24078        uploadedImages.forEach((file, idx) => {
24079            const reader = new FileReader();
24080            reader.onload = function(e) {
24081                const previewHTML = `
24082                    <div class="image-preview-item">
24083                        <img src="${e.target.result}" alt="Preview ${idx + 1}">
24084                        <button type="button" class="remove-image" data-index="${idx}" aria-label="Hapus">
24085                            <svg width="16" height="16" viewBox="0 0 24 24">
24086                                <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
24087                            </svg>
24088                        </button>
24089                    </div>
24090                `;
24091                previewGrid.insertAdjacentHTML('beforeend', previewHTML);
24092            };
24093            reader.readAsDataURL(file);
24094        });
24095
24096        calculatePotentialPoints();
24097
24098        setTimeout(() => {
24099            document.querySelectorAll('.remove-image').forEach(btn => {
24100                btn.addEventListener('click', function() {
24101                    const idx = parseInt(this.dataset.index);
24102                    removeImage(idx);
24103                });
24104            });
24105        }, 100);
24106    }
24107
24108    // Handle form submission
24109    async function handleSubmit(e) {
24110        e.preventDefault();
24111
24112        // Validate
24113        if (selectedRating === 0) {
24114            showError('Silakan pilih rating terlebih dahulu');
24115            return;
24116        }
24117
24118        const reviewText = document.getElementById('review-text').value.trim();
24119        if (reviewText.length < 20) {
24120            showError('Ulasan minimal 20 karakter');
24121            return;
24122        }
24123
24124        const submitBtn = document.getElementById('review-submit');
24125        submitBtn.disabled = true;
24126        
24127        try {
24128            // Step 1: Upload images if any
24129            let imageUrls = [];
24130            if (uploadedImages.length > 0) {
24131                submitBtn.textContent = 'Mengupload foto...';
24132                imageUrls = await uploadImages();
24133                if (!imageUrls) {
24134                    // Upload failed
24135                    submitBtn.disabled = false;
24136                    submitBtn.textContent = 'Kirim Ulasan';
24137                    return;
24138                }
24139            }
24140
24141            // Step 2: Submit review with image URLs
24142            submitBtn.textContent = 'Mengirim ulasan...';
24143            
24144            const payload = {
24145                csrf_token: getCsrfToken(),
24146                product_id: currentProductId,
24147                order_id: currentOrderId || 0,
24148                so_ref_id: currentSoRefId || undefined,
24149                rating: selectedRating,
24150                review_text: reviewText,
24151                is_anonymous: document.getElementById('review-anonymous').checked,
24152                images: imageUrls
24153            };
24154
24155            const response = await fetch('/api/reviews/submit.php', {
24156                method: 'POST',
24157                headers: {
24158                    'Content-Type': 'application/json',
24159                    'X-CSRF-Token': getCsrfToken()
24160                },
24161                body: JSON.stringify(payload)
24162            });
24163
24164            const result = await response.json();
24165
24166            if (result.success) {
24167                showSuccess(result.message || 'Ulasan berhasil dikirim!');
24168                closeReviewModal();
24169                
24170                // Refresh reviewable orders if on account page
24171                if (typeof loadReviewableOrders === 'function') {
24172                    setTimeout(loadReviewableOrders, 500);
24173                }
24174            } else {
24175                showError(result.message || 'Gagal mengirim ulasan');
24176            }
24177        } catch (error) {
24178            console.error('Submit error:', error);
24179            showError('Terjadi kesalahan saat mengirim ulasan');
24180        } finally {
24181            submitBtn.disabled = false;
24182            submitBtn.textContent = 'Kirim Ulasan';
24183        }
24184    }
24185
24186    /**
24187     * Upload images to server
24188     * @returns {Promise<Array|null>} Array of image URLs or null if failed
24189     */
24190    async function uploadImages() {
24191        if (uploadedImages.length === 0) {
24192            return [];
24193        }
24194
24195        const formData = new FormData();
24196        uploadedImages.forEach(file => {
24197            formData.append('images[]', file);
24198        });
24199
24200        try {
24201            const response = await fetch('/api/reviews/upload_images.php', {
24202                method: 'POST',
24203                headers: {
24204                    'X-CSRF-Token': getCsrfToken()
24205                },
24206                body: formData
24207            });
24208
24209            // Check if response is JSON
24210            const contentType = response.headers.get('content-type');
24211            if (!contentType || !contentType.includes('application/json')) {
24212                const text = await response.text();
24213                console.error('Non-JSON response:', text);
24214                showError('Server error: Response bukan JSON. Cek browser console untuk detail.');
24215                return null;
24216            }
24217
24218            const result = await response.json();
24219
24220            if (result.success && result.images) {
24221                // Extract URLs from uploaded images
24222                return result.images.map(img => img.url);
24223            } else {
24224                showError(result.message || 'Gagal mengupload foto');
24225                return null;
24226            }
24227        } catch (error) {
24228            console.error('Upload images error:', error);
24229            showError('Terjadi kesalahan saat mengupload foto. Cek console untuk detail.');
24230            return null;
24231        }
24232    }
24233
24234    // Load reviewable orders (for account page)
24235    async function loadReviewableOrders() {
24236        // Only run if on account/profile page
24237        const container = document.getElementById('reviewable-orders-list');
24238        const loading = document.getElementById('reviewable-orders-loading');
24239        const empty = document.getElementById('reviewable-orders-empty');
24240        
24241        if (!container) {
24242            return;
24243        }
24244
24245        // Show loading
24246        if (loading) loading.style.display = 'block';
24247        if (empty) empty.style.display = 'none';
24248        container.innerHTML = '';
24249
24250        try {
24251            const response = await fetch('/api/reviews/reviewable.php');
24252            const result = await response.json();
24253
24254            if (loading) loading.style.display = 'none';
24255
24256            if (result.success && result.data.length > 0) {
24257                renderReviewableOrders(result.data);
24258                if (empty) empty.style.display = 'none';
24259            } else {
24260                if (empty) empty.style.display = 'block';
24261            }
24262        } catch (error) {
24263            console.error('Load reviewable orders error:', error);
24264            if (loading) loading.style.display = 'none';
24265            if (empty) {
24266                empty.innerHTML = '<p>Gagal memuat data. Silakan refresh halaman.</p>';
24267                empty.style.display = 'block';
24268            }
24269        }
24270    }
24271
24272    // Render reviewable orders
24273    function renderReviewableOrders(orders) {
24274        const container = document.getElementById('reviewable-orders-list');
24275        if (!container) return;
24276
24277        let html = '';
24278        
24279        orders.forEach(order => {
24280            html += `
24281                <div class="reviewable-order-item">
24282                    <img src="${escapeHtml(order.product_image || '/assets/img/no-image.png')}" alt="${escapeHtml(order.product_name)}" class="roi-image">
24283                    <div class="roi-info">
24284                        <h4>${escapeHtml(order.product_name)}</h4>
24285                        <p class="roi-meta">Order: ${escapeHtml(order.order_number)}</p>
24286                        <p class="roi-meta">Dibeli: ${escapeHtml(order.order_date)}</p>
24287                    </div>
24288                    <button type="button" 
24289                            class="btn btn-primary btn-write-review"
24290                            data-product-id="${order.product_id}"
24291                            data-order-id="${order.order_id || ''}"
24292                            data-so-ref-id="${escapeHtml(order.so_ref_id || '')}"
24293                            data-product-name="${escapeHtml(order.product_name)}"
24294                            data-product-image="${escapeHtml(order.product_image || '')}"
24295                            data-product-sku="${escapeHtml(order.product_sku || '')}">
24296                        ✍️ Tulis Ulasan
24297                    </button>
24298                </div>
24299            `;
24300        });
24301        
24302        container.innerHTML = html;
24303    }
24304
24305    // Escape HTML for XSS protection
24306    function escapeHtml(text) {
24307        const div = document.createElement('div');
24308        div.textContent = text;
24309        return div.innerHTML;
24310    }
24311
24312    // Show error
24313    function showError(message) {
24314        const errorEl = document.getElementById('review-form-error');
24315        errorEl.textContent = message;
24316        errorEl.style.display = 'block';
24317        
24318        setTimeout(() => {
24319            errorEl.style.display = 'none';
24320        }, 5000);
24321    }
24322
24323    // Show success
24324    function showSuccess(message) {
24325        // Create toast notification
24326        const toast = document.createElement('div');
24327        toast.className = 'toast-notification success';
24328        toast.textContent = message;
24329        toast.style.cssText = 'position:fixed;top:20px;right:20px;background:#059669;color:white;padding:16px 24px;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.15);z-index:10000;font-size:14px;font-weight:500;';
24330        
24331        document.body.appendChild(toast);
24332        
24333        setTimeout(() => {
24334            toast.style.opacity = '0';
24335            toast.style.transition = 'opacity 0.3s';
24336            setTimeout(() => toast.remove(), 300);
24337        }, 3000);
24338    }
24339
24340    // Mask user name for privacy preview (like PHP maskReviewerName)
24341    function maskUserName(fullName) {
24342        if (!fullName || fullName.trim() === '') {
24343            return 'Anonymous';
24344        }
24345        
24346        const words = fullName.trim().split(/\s+/);
24347        const maskedWords = words.map(word => {
24348            const len = word.length;
24349            if (len <= 1) return word;
24350            return word.charAt(0) + '*'.repeat(len - 1);
24351        });
24352        
24353        return maskedWords.join(' ');
24354    }
24355
24356    // Update privacy preview text with user's actual masked name
24357    function updatePrivacyPreview() {
24358        const previewEl = document.getElementById('review-anonymous-preview');
24359        if (!previewEl) return;
24360        
24361        const userName = window.userFullName || '';
24362        if (userName) {
24363            const maskedName = maskUserName(userName);
24364            previewEl.textContent = `Nama Anda akan ditampilkan sebagai "${maskedName}" untuk menjaga privasi`;
24365        } else {
24366            previewEl.textContent = 'Nama Anda akan ditampilkan de
24366ngan disamarkan untuk menjaga privasi';
24367        }
24368    }
24369
24370    // Initialize when DOM ready
24371    if (document.readyState === 'loading') {
24372        document.addEventListener('DOMContentLoaded', init);
24373    } else {
24374        init();
24375    }
24376
24377    // Export openReviewModal to global scope for use in other scripts
24378    window.openReviewModal = openReviewModal;
24379
24380})();
24381
24382</script>
24382
24383    <style>
24384/* =========================================================
24385   ⚡ FIXED FOOTER – RED ELEGANT MODE
24386========================================================= */
24387
24388.sbo-top-fixed-footer{
24389      position: fixed;
24390    left: 0;
24391    right: 0;
24392    bottom: 0;
24393    z-index: 999;
24394    display: flex;
24395    align-items: center;
24396    justify-content: space-around;
24397    gap: 8px;
24398    padding: 11px 12px calc(11px + env(safe-area-inset-bottom));
24399    overflow: hidden;
24400    background: radial-gradient(circle at 50% 0%, #000000, rgb(0 0 0 / 92%) 58%, rgb(0 0 0 / 98%)), linear-gradient(180deg, rgba(25, 0, 0, 0.92), rgb(0 0 0 / 98%));
24401    backdrop-filter: blur(12px);
24402    border-top: 1px solid rgb(143 1 1);
24403    border-radius: 18px 18px 0 0;
24404    box-shadow: 0 -12px 30px #410000, inset 0 1px 0 rgba(255, 255, 255, .05);
24405    font-family: "Orbitron", "Poppins", system-ui, sans-serif;
24406}
24407
24408/* scanline */
24409.sbo-top-fixed-footer::before{
24410  content:"";
24411  position:absolute;
24412  inset:0;
24413  pointer-events:none;
24414  background:
24415    repeating-linear-gradient(
24416      0deg,
24417      rgba(0,0,0,.015),
24418      rgba(0,0,0,.015) 1px,
24419      transparent 1px,
24420      transparent 6px
24421    );
24422  opacity:.85;
24423  mix-blend-mode:overlay;
24424}
24425
24426/* shine sweep */
24427.sbo-top-fixed-footer::after{
24428  content:"";
24429  position:absolute;
24430  top:0;
24431  left:-160%;
24432  width:60%;
24433  height:100%;
24434  background: linear-gradient(120deg, transparent, rgba(255, 90, 90, 0.25), transparent);
24435  transform: skewX(-25deg);
24436  animation: footerFinalShine 3.8s infinite;
24437  mix-blend-mode: screen;
24438  pointer-events:none;
24439}
24440
24441@keyframes footerFinalShine{
24442  0%{left:-160%;}
24443  68%{left:160%;}
24444  100%{left:160%;}
24445}
24446
24447/* =========================================================
24448   ITEM
24449========================================================= */
24450
24451.sbo-top-fixed-footer a{
24452  flex:1;
24453  max-width:110px;
24454
24455  display:flex;
24456  flex-direction:column;
24457  align-items:center;
24458  justify-content:center;
24459  gap:6px;
24460
24461  padding: 8px 6px;
24462
24463  text-decoration:none;
24464  text-align:center;
24465
24466  color: rgba(171 0 0);
24467  font-size: 12px;
24468  font-weight: 800;
24469  letter-spacing: .25px;
24470
24471  border-radius: 14px;
24472  position:relative;
24473  z-index:1;
24474
24475  transition: all .18s ease;
24476}
24477
24478.sbo-top-fixed-footer a:hover{
24479  color:#ffffff;
24480  background: rgba(255, 80, 80, 0.08);
24481  filter: drop-shadow(0 0 12px rgba(255, 80, 80, 0.15));
24482  transform: translateY(-1px);
24483}
24484
24485.sbo-top-fixed-footer a:active{
24486  transform: translateY(0) scale(.99);
24487}
24488
24489/* =========================================================
24490   ICON
24491========================================================= */
24492
24493.sbo-top-fixed-footer img{
24494  width:26px;
24495  height:26px;
24496  transition: .18s ease;
24497  filter: drop-shadow(0 0 8px rgba(255, 80, 80, 0.35));
24498}
24499
24500.sbo-top-fixed-footer a:hover img{
24501  transform: scale(1.08);
24502  filter: drop-shadow(0 0 16px rgba(255, 80, 80, 0.6));
24503}
24504
24505/* =========================================================
24506   HUD CORNERS
24507========================================================= */
24508
24509.sbo-top-fixed-footer .hud-corner{
24510  position:absolute;
24511  width:18px;height:18px;
24512  pointer-events:none;
24513  border: 1px solid rgba(255, 90, 90, 0.5);
24514  filter: drop-shadow(0 0 10px rgba(255, 80, 80, 0.2));
24515  opacity:.85;
24516}
24517
24518.sbo-top-fixed-footer .hud-tl{top:8px;left:10px;border-right:none;border-bottom:none;border-radius:10px 0 0 0;}
24519.sbo-top-fixed-footer .hud-tr{top:8px;right:10px;border-left:none;border-bottom:none;border-radius:0 10px 0 0;}
24520.sbo-top-fixed-footer .hud-bl{bottom:8px;left:10px;border-right:none;border-top:none;border-radius:0 0 0 10px;}
24521.sbo-top-fixed-footer .hud-br{bottom:8px;right:10px;border-left:none;border-top:none;border-radius:0 0 10px 0;}
24522
24523/* =========================================================
24524   CENTER CTA
24525========================================================= */
24526
24527.sbo-top-fixed-footer .tada{
24528      color: #ffffff;
24529    background: radial-gradient(circle at 50% 0%, #f52b2b, rgba(0, 0, 0, 0) 70%), rgba(255, 60, 60, 0.08);
24530    border: 1px solid rgba(255, 90, 90, 0.35);
24531    box-shadow: 0 0 18px #f52b2b, inset 0 0 16px rgba(255, 60, 60, 0.15);
24532}
24533
24534.sbo-top-fixed-footer .tada img{
24535  width:30px;
24536  height:30px;
24537  filter: drop-shadow(0 0 16px rgba(255, 80, 80, 0.65));
24538}
24539
24540/* pulse */
24541.sbo-top-fixed-footer .tada::before{
24542  content:"";
24543  position:absolute;
24544  inset:-12px;
24545  border-radius:999px;
24546  background:
24547    radial-gradient(circle, #000000, transparent 70%),
24548    radial-gradient(circle, rgba(255, 120, 120, 0.08), transparent 72%);
24549  filter: blur(12px);
24550  opacity:.55;
24551  animation: corePulse 2.9s ease-in-out infinite;
24552  z-index:-1;
24553}
24554
24555@keyframes corePulse{
24556  0%,100%{opacity:.35; transform:scale(.99);}
24557  50%{opacity:.75; transform:scale(1.02);}
24558}
24559
24560/* ACTIVE */
24561.sbo-top-fixed-footer a.is-active{
24562  color:#ffffff;
24563  background: rgba(255, 80, 80, 0.1);
24564  box-shadow: inset 0 0 0 1px rgba(255, 90, 90, 0.25);
24565}
24566
24567.sbo-top-fixed-footer a.is-active img{
24568  filter: drop-shadow(0 0 18px rgba(255, 80, 80, 0.8));
24569}
24570
24571/* RESPONSIVE */
24572@media (max-width:500px){
24573  .sbo-top-fixed-footer{
24574    padding: 10px 10px calc(10px + env(safe-area-inset-bottom));
24575    border-radius: 16px 16px 0 0;
24576    gap:6px;
24577  }
24578  .sbo-top-fixed-footer a{
24579    font-size:11px;
24580    padding:7px 5px;
24581    gap:5px;
24582    max-width:104px;
24583  }
24584  .sbo-top-fixed-footer img{width:22px;height:22px;}
24585  .sbo-top-fixed-footer .tada img{width:26px;height:26px;}
24586}
24587</style>
24588<div class="sbo-top-fixed-footer">
24589  <a href="https://slot-gacor.loklokpro.com/" target="_blank" rel="nofollow noopener">
24590    <img src="https://imgmahasuhu.io/images/2026/05/13/iconbonus.png" alt="BONUS IWANTOGEL">
24591    Promosi
24592  </a>
24593  <a href="https://slot-gacor.loklokpro.com/" target="_blank" rel="nofollow noopener">
24594    <img src="https://imgmahasuhu.io/images/2026/05/13/iconlogin.png" alt="LOGIN IWANTOGEL">
24595    Login
24596  </a>
24597  <a href="https://slot-gacor.loklokpro.com/" target="_blank" rel="nofollow noopener" class="tada">
24598    <img src="https://imgmahasuhu.io/images/2026/05/13/icondaftar.webp" alt="DAFTAR IWANTOGEL">
24599    Daftar
24600  </a>
24601  <a href="https://slot-gacor.loklokpro.com/" target="_blank" rel="nofollow noopener">
24602    <img src="https://imgmahasuhu.io/images/2026/05/13/iconlinkalternatif.png" alt="LINK ALTERNATIF IWANTOGEL">
24603    Alternatif
24604  </a>
24605  <a href="https://slot-gacor.loklokpro.com/" target="_blank" rel="nofollow noopener">
24606    <img src="https://imgmahasuhu.io/images/2026/05/13/iconlivechat.png" alt="LIVE CHAT IWANTOGEL">
24607    Live Chat
24608  </a>
24609</div>
24610<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"51df3b17a746455f940c0b561e8c0c0b","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24610
24610
24611<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"51df3b17a746455f940c0b561e8c0c0b","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24611
24611
24612<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"51df3b17a746455f940c0b561e8c0c0b","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24612
24612
24613<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"51df3b17a746455f940c0b561e8c0c0b","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24613
24613
24614<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"51df3b17a746455f940c0b561e8c0c0b","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24614
24614
24615<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"f2ab780bc9a44e979f5cc2ae3699b18e","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24615
24615
24616<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"5b1c76696dc94f7db08316e2401cd14e","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24616
24616
24617<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v3d52b47920f24c319d37e2661827c42b1787588026925" integrity="sha512-d9sL6GJLXn6fInD1+TVXhTcQOsmxeHfmHAvwGDIxp5TO+uo1fiWW7mHomMj4MLRlCsJDTqXzWLHJFFlPCEIj/A==" data-cf-beacon='{"version":"2024.11.0","token":"870082c1b60640b78980af04df5a944d","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24617
24617
24618<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v3d52b47920f24c319d37e2661827c42b1787588026925" integrity="sha512-d9sL6GJLXn6fInD1+TVXhTcQOsmxeHfmHAvwGDIxp5TO+uo1fiWW7mHomMj4MLRlCsJDTqXzWLHJFFlPCEIj/A==" data-cf-beacon='{"version":"2024.11.0","token":"9633684236f448248e0f65d50ee7e4af","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24618
24618
24619<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v3d52b47920f24c319d37e2661827c42b1787588026925" integrity="sha512-d9sL6GJLXn6fInD1+TVXhTcQOsmxeHfmHAvwGDIxp5TO+uo1fiWW7mHomMj4MLRlCsJDTqXzWLHJFFlPCEIj/A==" data-cf-beacon='{"version":"2024.11.0","token":"31d3901e3a0a40c687e8d44f28060815","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24619
24619
24620<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v3d52b47920f24c319d37e2661827c42b1787588026925" integrity="sha512-d9sL6GJLXn6fInD1+TVXhTcQOsmxeHfmHAvwGDIxp5TO+uo1fiWW7mHomMj4MLRlCsJDTqXzWLHJFFlPCEIj/A==" data-cf-beacon='{"version":"2024.11.0","token":"28460a7e2fdb467898d7a1cc07bfbc86","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24620
24620
24621<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v3d52b47920f24c319d37e2661827c42b1787588026925" integrity="sha512-d9sL6GJLXn6fInD1+TVXhTcQOsmxeHfmHAvwGDIxp5TO+uo1fiWW7mHomMj4MLRlCsJDTqXzWLHJFFlPCEIj/A==" data-cf-beacon='{"version":"2024.11.0","token":"ca8a0c1b183e4b2b9814a6d6944e175f","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24621
24621
24622<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v3d52b47920f24c319d37e2661827c42b1787588026925" integrity="sha512-d9sL6GJLXn6fInD1+TVXhTcQOsmxeHfmHAvwGDIxp5TO+uo1fiWW7mHomMj4MLRlCsJDTqXzWLHJFFlPCEIj/A==" data-cf-beacon='{"version":"2024.11.0","token":"0fa09fdd1e6248a88a5f9fcc43d3662c","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24622
24622
24623<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v3d52b47920f24c319d37e2661827c42b1787588026925" integrity="sha512-d9sL6GJLXn6fInD1+TVXhTcQOsmxeHfmHAvwGDIxp5TO+uo1fiWW7mHomMj4MLRlCsJDTqXzWLHJFFlPCEIj/A==" data-cf-beacon='{"version":"2024.11.0","token":"660e3e1485aa4d0bb4239656395758ff","r":1}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24623
24623
24624<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"3980ac8b91ca47b4838c0f3feb47c836","r":1,"spa":2}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24624
24624
24625<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"85fdc56030fd427cacac1d2d2af9360b","r":1,"spa":2}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24625
24625
24626<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"ee5eae759d7945b7a73a25243248a01d","r":1,"spa":2}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24626
24626
24627<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"45ca011ed7314b63a70a1eaf042a5365","r":1,"spa":2}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 24627
24627
24628<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"45ca011ed7314b63a70a1eaf042a5365","r":1,"spa":2}' crossorigin="anonymous"></script>
24628
24629</body>
24630</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.