PageSourceSearch

https://norita.mx/

html norita.mx collected 2026-10-09 23:23:46 UTC 830,918 bytes, 24,546 lines download raw bytes

1
2
3<!DOCTYPE html>
4<html lang="id">
5<head>
6  <meta charset="UTF-8">
7  <title>EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap</title>
8  <meta name="description" content="Rasakan sensasi relaksasi premium bersama EDUTOTO melalui akses yang cepat dan fitur yang lengkap. Dengan tampilan yang modern, setiap aktivitas bermain menjadi lebih nyaman dan menyenangkan.">
9  <meta name="robots" content="index,follow,nosnippet">
10  <link rel="canonical" href="https://norita.mx/">
11  <link rel="amphtml" href="https://relaksasi.norita.mx/">
12  <link rel="alternate" href="https://relaksasi.norita.mx/" hreflang="id">
13  <link rel="alternate" href="https://relaksasi.norita.mx/" hreflang="en">
14  <link rel="alternate" href="https://relaksasi.norita.mx/" hreflang="x-default">
15  <link rel="alternate" hreflang="id-ID" href="https://relaksasi.norita.mx/" />
16  <link rel="alternate" hreflang="en-US" href="https://relaksasi.norita.mx/" />
17  <link rel="alternate" hreflang="en-SG" href="https://relaksasi.norita.mx/" />
18    <link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
vendor: 67 bytes, lines 18-19
18
19  <script async src="https://www.googletagmanager.com/gtag/js?id=
19G-0LSM1YJSPZ
vendor: 48 bytes, lines 19-20
19" nonce="se3iyzEHMc9FQEB4wSyd0w=="></script>
20  
20<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>
20
21  <meta property="og:locale" content="id_ID">
22  <meta property="og:site_name" content="EDUTOTO">
23  <meta property="og:type" content="product">
24  <meta property="og:title" content="EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap">
25  <meta property="og:description" content="Rasakan sensasi relaksasi premium bersama EDUTOTO melalui akses yang cepat dan fitur yang lengkap. Dengan tampilan yang modern, setiap aktivitas bermain menjadi lebih nyaman dan menyenangkan.">
26  <meta property="og:url" content="https://norita.mx/">
27  <meta property="og:image" content="img/banner-edutoto.webp">
28  <meta property="og:image:alt" content="EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap">
29<meta property="product:price:amount" content="10000">
30<meta property="product:price:currency" content="IDR">
31<meta property="product:availability" content="out of stock">
32<meta name="twitter:card" content="summary_large_image">
33<meta name="twitter:title" content="EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap">
34<meta name="twitter:description" content="Rasakan sensasi relaksasi premium bersama EDUTOTO melalui akses yang cepat dan fitur yang lengkap. Dengan tampilan yang modern, setiap aktivitas bermain menjadi lebih nyaman dan menyenangkan.">
35<meta name="twitter:image" content="img/banner-edutoto.webp">
36
37<script type="application/ld+json">
38      {
39        "@context": "http:\/\/schema.org",
40        "@type": "BreadcrumbList",
41        "itemListElement": [
42          {
43            "@type": "ListItem",
44            "item": {
45              "@id": "https://norita.mx/",
46              "name": "EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap"
47            },
48            "position": 1
49          }
50        ]
51      }
52    </script>
52
53    
53<script type="application/ld+json">
54      {
55        "@context": "http:\/\/schema.org",
56        "@type": "Product",
57        "name": "EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap",
58        "description": "Rasakan sensasi relaksasi premium bersama EDUTOTO melalui akses yang cepat dan fitur yang lengkap. Dengan tampilan yang modern, setiap aktivitas bermain menjadi lebih nyaman dan menyenangkan.",
59        "image": "img/banner-edutoto.webp",
60        "offers": {
61          "@type": "http:\/\/schema.org\/Offer",
62          "price": 9999,
63          "url": "https://norita.mx/",
64          "priceCurrency": "IDR",
65          "availability": "http:\/\/schema.org\/InStock"
66        }
67      }
68    </script>
68
69    
69<script type="application/ld+json">
70      {
71        "@context": "http:\/\/schema.org\/",
72        "@type": "WebPage",
73        "speakable": {
74          "@type": "SpeakableSpecification",
75          "cssSelector": [".description"],
76          "xpath": ["\/html\/head\/title"]
77        }
78      }
79    </script>
79
80    <!-- ZS -->
81    <!-- Schema JSON untuk SBO -->
82<script type="application/ld+json">
83{
84"@context": "https://schema.org/",
85"@type": "Product",
86"name": "EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap",
87"url": "https://norita.mx/",
88"sku": "EDUTOTO",
89"image": [
90  "img/banner-edutoto.webp"
91],
92"description": "Rasakan sensasi relaksasi premium bersama EDUTOTO melalui akses yang cepat dan fitur yang lengkap. Dengan tampilan yang modern, setiap aktivitas bermain menjadi lebih nyaman dan menyenangkan.",
93"brand": {
94  "@type": "Organization",
95  "name": "EDUTOTO LOGIN"
96},
97"offers": {
98  "@type": "Offer",
99  "name": "EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap",
100  "sku": "EDUTOTO LOGIN",
101  "price": "9999",
102  "priceCurrency": "IDR",
103  "availability": "https://schema.org/InStock"
104}
105}
106</script>
106
107
108<script type="application/ld+json">
109{
110"@context": "https://schema.org/",
111"@type": "Product",
112"name": "EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap",
113"image": "img/banner-edutoto.webp",
114"description": "Rasakan sensasi relaksasi premium bersama EDUTOTO melalui akses yang cepat dan fitur yang lengkap. Dengan tampilan yang modern, setiap aktivitas bermain menjadi lebih nyaman dan menyenangkan.",
115"sku": "EDUTOTO LOGIN",
116"offers": {
117  "@type": "Offer",
118  "priceCurrency": "IDR",
119  "url": "https://norita.mx/",
120  "availability": "https://schema.org/InStock",
121  "price": "9999.00"
122}
123}
124</script>
124
125
126<script type="application/ld+json" id="bv-jsonld-bvloader-summary">
127{
128"@context": "https://schema.org/",
129"@id": "https://norita.mx/",
130"aggregateRating": {
131  "@type": "AggregateRating",
132  "itemReviewed": {
133    "@type": "IndividualProduct",
134    "name": "EDUTOTO"
135  },
136  "ratingValue": "5.0",
137  "reviewCount": 954638,
138  "bestRating": 5
139}
140}
141</script>
141
142
143<script type="application/ld+json">
144{
145"@context": "https://schema.org",
146"@type": "BreadcrumbList",
147"itemListElement": [
148  {
149    "@type": "ListItem",
150    "position": 1,
151    "item": {
152      "@id": "https://norita.mx/",
153      "name": "EDUTOTO"
154    }
155  },
156  {
157    "@type": "ListItem",
158    "position": 2,
159    "item": {
160      "@id": "https://norita.mx/",
161      "name": "EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap"
162    }
163  },
164  {
165    "@type": "ListItem",
166    "position": 3,
167    "item": {
168      "@id": "https://norita.mx/",
169      "name": "EDUTOTO LOGIN"
170    }
171  },
172  {
173    "@type": "ListItem",
174    "position": 4,
175    "item": {
176      "@id": "https://norita.mx/",
177      "name": "EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap"
178    }
179  }
180]
181}
182</script>
182
183
184<script type="application/ld+json" id="bv-jsonld-reviews-data">
185{
186  "@context": "https://schema.org",
187  "@type": "Organization",
188  "@id": "https://norita.mx/",
189  "name": "EDUTOTO",
190  "alternateName": "EDUTOTO",
191  "url": "https://norita.mx/",
192  "review": [
193    {
194      "@type": "Review",
195      "dateCreated": "2026-03-31T12:28:35+00:00",
196      "datePublished": "2026-03-31T12:28:35+00:00",
197      "headline": "Akses Cepat Sejak Awal",
198      "reviewBody": "Sejak pertama masuk, EDUTOTO terasa nyaman. Menu-menunya rapi, aksesnya cepat, dan saya bisa langsung bermain tanpa ribet.",
199      "reviewRating": {
200        "@type": "Rating",
201        "ratingValue": 5,
202        "bestRating": 5,
203        "worstRating": 1
204      },
205      "author": {
206        "@type": "Person",
207        "name": "Raka Putra – Bandung"
208      }
209    },
210    {
211      "@type": "Review",
212      "dateCreated": "2026-04-02T18:28:21+00:00",
213      "datePublished": "2026-04-03T18:28:21+00:00",
214      "headline": "Relaksasi yang Terasa Nyata",
215      "reviewBody": "Tampilannya elegan dan tidak bikin pusing. Saya main di HP setiap hari, alurnya ringan, dan suasana relaksasinya benar-benar terasa.",
216      "reviewRating": {
217        "@type": "Rating",
218        "ratingValue": 5,
219        "bestRating": 5,
220        "worstRating": 1
221      },
222      "author": {
223        "@type": "Person",
224        "name": "Nadia Kusuma – Surabaya"
225      }
226    },
227    {
228      "@type": "Review",
229      "dateCreated": "2026-04-05T19:28:18+00:00",
230      "datePublished": "2026-04-05T19:28:18+00:00",
231      "headline": "Stabil dan Praktis",
232      "reviewBody": "Yang bikin saya betah di EDUTOTO adalah sistemnya stabil dan fiturnya lengkap. Login sampai transaksi terasa praktis, jadi mainnya lebih tenang.",
233      "reviewRating": {
234        "@type": "Rating",
235        "ratingValue": 5,
236        "bestRating": 5,
237        "worstRating": 1
238      },
239      "author": {
240        "@type": "Person",
241        "name": "Fajar Santoso – Yogyakarta"
242      }
243    }
244  ]
245}
246</script>
vendor: 2 bytes, line 246
246
247<script type="application/ld+json">
248{
249  "@context": "https://schema.org",
250  "@type": "FAQPage",
251  "mainEntity": [
252    {
253      "@type": "Question",
254      "name": "Apa yang membuat pengalaman EDUTOTO terasa lebih premium?",
255      "acceptedAnswer": {
256        "@type": "Answer",
257        "text": "EDUTOTO memadukan desain elegan, akses yang ringkas, dan fitur lengkap dalam satu platform. Kombinasi itu membuat setiap sesi terasa lebih tenang, nyaman, dan berkelas sejak pengguna pertama kali masuk."
258      }
259    },
260    {
261      "@type": "Question",
262      "name": "Apakah pemula bisa langsung bermain di EDUTOTO?",
263      "acceptedAnswer": {
264        "@type": "Answer",
265        "text": "Bisa. Antarmuka EDUTOTO sederhana dan modern, sehingga pemula tidak perlu melalui langkah yang berbelit. Menu favorit mudah ditemukan, dan alur bermain tetap jelas bagi pemain yang sudah aktif."
266      }
267    },
268    {
269      "@type": "Question",
270      "name": "Bagaimana EDUTOTO menjaga suasana relaksasi saat bermain?",
271      "acceptedAnswer": {
272        "@type": "Answer",
273        "text": "Nuansa relaksasi dijaga lewat tampilan yang rapi, pilihan game berkualitas, dan sistem yang selalu diperbarui. Performa yang stabil membuat sesi bermain terasa tenang tanpa gangguan yang mengganggu kenyamanan."
274      }
275    },
276    {
277      "@type": "Question",
278      "name": "Apakah EDUTOTO bisa dibuka di HP dan komputer?",
279      "acceptedAnswer": {
280        "@type": "Answer",
281        "text": "Ya. EDUTOTO mendukung akses di smartphone maupun komputer. Tampilan responsif, navigasi yang intuitif, dan proses transaksi yang ringan membuat login hingga bermain tetap praktis di perangkat mana pun."
282      }
283    },
284    {
285      "@type": "Question",
286      "name": "Mengapa EDUTOTO cocok dijadikan pilihan utama?",
287      "acceptedAnswer": {
288        "@type": "Answer",
289        "text": "EDUTOTO cocok karena menawarkan akses cepat, fitur premium, dan reputasi positif. Bagi siapa pun yang mencari platform bermain yang lebih santai dan terpercaya, pengalaman di sini terasa lebih nyaman sekaligus meyakinkan."
290      }
291    }
292  ]
293}
294</script>
294
295  <!-- User Session Data -->
296    <meta name="csrf-token" content="9ce6e4a17f7cc189893df9e47789b13e98829450d516a0cae5ca7b5f43e5dadc">
297  <meta name="user-logged-in" content="false">
298  <meta name="user-id" content="0">
299  
300  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
301
302  <link rel="shortcut icon" href="/img/favicon.png" type="image/x-icon">
303  <style data-inlined="style.css">
304@font-face {
305    font-family: 'Segoe UI';
306    src: url('/assets/fonts/segoeui.woff2') format('woff2'),
307         url('/assets/fonts/segoeui.woff') format('woff');
308    font-weight: 400;
309    font-style: normal;
310}
311
312:root {
313  --brand-50:#FFFBEB;
314  --brand-100:#FEF3C7;
315  --brand-500:#C9A227;
316  --brand-600:#8B6914;
317  --gray-900:#1F1F23;
318  --gray-700:#3D3F45;
319  --gray-500:#6F737B;
320  --gray-300:#C5C9CF;
321  --gray-100:#F3F5F7;
322  --border:#E4E7EB;
323  --radius:8px;
324  --transition: .18s ease;
325  /* font-family: "Inter", system-ui, sans-serif; */
326  font-family: 'Segoe UI', sans-serif;
327}
328
329* { box-sizing: border-box; }
330body { 
331  margin:0; 
332  padding-bottom: 0; /* Reset for desktop */
333  color:var(--gray-900); 
334  background:#fff; 
335}
336img { max-width:100%; display:block; }
337
338.container {
339  width:100%;
340  max-width:1280px;
341  margin:0 auto;
342  padding:10px 0;
343}
344
345.topbar {
346  background:#111;
347  color:#fff;
348  font-size:13px;
349}
350.topbar-inner {
351  display:flex;
352  justify-content:space-between;
353  align-items:center;
354  min-height:36px;
355}
356.topbar a { color:#fff; text-decoration:none; margin-left:12px; }
357
358/* ========== Desktop base (Glassmorphism Modern with Warm Gradient) ========== */
359.navbar {
360  background:linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(255,251,235,0.85) 100%);
361  backdrop-filter:blur(12px);
362  -webkit-backdrop-filter:blur(12px);
363  border-bottom:1px solid rgba(228,231,235,0.6);
364  box-shadow:0 2px 16px -4px rgba(0,0,0,0.08);
365  position:sticky;
366  top:0;
367  z-index:500;
368  transition:all .3s ease;
369}
370
371/* Accent line gradient gold */
372.navbar::after {
373  content:'';
374  position:absolute;
375  bottom:0;
376  left:0;
377  right:0;
378  height:2px;
379  background:linear-gradient(90deg,transparent 0%, var(--brand-500) 20%, var(--brand-600) 50%, var(--brand-500) 80%, transparent 100%);
380  opacity:0.5;
381}
382
383.nav-inner {
384  display:flex;
385  align-items:center;
386  gap:24px;
387  min-height:70px;
388}
389.nav-inner .search .m-search-ico { display: none; }
390
391/* Secara default (desktop) ikon login disembunyikan; teks muncul */
392.login-btn .login-ico { display:none; }
393.navbar-logo {
394  display: none;
395  background:linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(255,251,235,0.85) 100%);
396  backdrop-filter:blur(12px);
397  -webkit-backdrop-filter:blur(12px);
398  border-bottom:1px solid rgba(228,231,235,0.6);
399  box-shadow:0 2px 16px -4px rgba(0,0,0,0.08);
400  position:sticky;
401  top:0;
402  z-index:500;
403}
404
405/* Hide bottom navigation on desktop - only show on mobile */
406.bottom-nav {
407  display: none;
408}
409
410/* ==============================================================
411   MOBILE (<=640px)
412   ============================================================== */
413@media (max-width:640px){
414  .container { padding:10px 16px; }
415  
416  .topbar { display:none !important; }
417
418  /* Add padding bottom for sticky bottom nav */
419  body {
420    padding-bottom: 68px; /* Height of bottom nav + safe area */
421  }
422
423  /* Mobile Top Navbar - Clean & Minimal */
424  .navbar {
425    background:linear-gradient(135deg, rgba(255,255,255,0.97) 0%, rgba(255,251,235,0.9) 100%);
426    backdrop-filter:blur(16px);
427    -webkit-backdrop-filter:blur(16px);
428    box-shadow:0 2px 12px -3px rgba(0,0,0,0.08);
429    border: 0;
430  }
431
432  /* Simple grid layout - Logo, Search, Category */
433  .nav-inner {
434    display:grid;
435    grid-template-columns:1fr;
436    gap:10px;
437    padding:12px 16px 0 12px;
438    min-height:auto;
439  }
440
441  /* Logo centered at top */
442  .nav-inner .logo {
443    justify-self:center;
444    margin:0;
445    display:flex;
446  }
447  .nav-inner .logo img { height:36px; }
448
449  /* Search bar - full width */
450  .nav-inner .search {
451    position:relative;
452    display:flex;
453    align-items:center;
454    gap:0;
455    width:100%;
456    height:44px;
457    background:#fff;
458    border:1.5px solid #e5e7eb;
459    border-radius:12px;
460    padding:0 12px;
461    box-shadow:0 1px 3px rgba(0,0,0,0.04);
462    transition:all .25s;
463  }
464  .nav-inner .search:focus-within {
465    border-color:#FBBF24;
466    box-shadow:0 0 0 3px rgba(251, 191, 36, 0.1);
467  }
468
469  .nav-inner .search-icon-btn {
470    color:#9ca3af;
471    padding:4px;
472    margin-right:4px;
473  }
474  
475  .nav-inner .search-icon-btn svg {
476    width:18px;
477    height:18px;
478  }
479
480  .nav-inner .search input {
481    flex:1;
482    min-width:0;
483    border:none;
484    outline:none;
485    background:transparent;
486    font-size:14px;
487    padding:0 8px;
488    height:100%;
489  }
490  
491  .nav-inner .ai-bot-toggle {
492    padding:6px;
493    margin-left:0;
494  }
495  
496  .nav-inner .ai-bot-toggle .bot-icon {
497    width:20px;
498    height:20px;
499  }
500  
501  .nav-inner .search-suggest {
502    left:0;
503    right:0;
504    width:calc(100vw - 32px);
505    margin-top:6px;
506    border-radius:12px;
507    max-height:70vh;
508  }
509
510  /* Hide desktop nav actions but keep structure for userMenu */
511  .nav-inner .nav-actions {
512    display:flex !important; /* Keep flex to allow userMenu positioning */
513    visibility:hidden; /* Hide visually but keep in DOM */
514    pointer-events:none; /* Disable interactions with hidden elements */
515    position:absolute; /* Remove from flow */
516    right:0;
517    top:0;
518  }
519  
520  /* Re-enable userMenu for logged-in users */
521  .nav-inner .nav-actions #userMenu {
522    visibility:visible !important;
523    pointer-events:auto !important;
524  }
525  
526  .nav-inner .logo { display: none; }
527  .navbar-logo {
528    display: block;
529    /* background:linear-gradient(135deg, rgba(255,255,255,0.97) 0%, rgba(255,251,235,0.9) 100%); */
530    backdrop-filter:blur(16px);
531    -webkit-backdrop-filter:blur(16px);
532    position: static; /* Remove sticky on mobile */
533    box-shadow: none;
534    border-bottom: 0;
535    padding-top: .6rem;
536  }
537  .nav-inner .logo-mobile { display: block; }
538  
539  /* Category menu wrapper will handle visibility */
540
541  /* ==============================================================
542     BOTTOM NAVIGATION - Styles moved to bottom-nav.css
543     ============================================================== */
544  /* All bottom-nav styles now in /assets/css/bottom-nav.css */
545
546  /* Hide desktop elements that now moved to bottom nav */
547  #wishlistBtn,
548  #notifBtn,
549  #loginBtn { display:none !important; }
550  
551  /* Hide desktop user menu on mobile - use bottom sheet instead */
552  .navbar #userMenu { 
553    display:none !important;
554  }
555  
556  .nav-actions .cart-indicator,
557  .nav-actions .compare-indicator {
558    display:none !important;
559  }
560
561  /* Hide category button from bottom nav */
562  #bottomCategoryBtn {
563    display: none !important;
564  }
565  
566  /* User menu button styling for mobile - scoped to navbar only */
567  .navbar #userMenu .login-btn.logged-in {
568    width:40px;
569    height:40px;
570    padding:0;
571    border-radius:50%;
572    display:flex;
573    align-items:center;
574    justify-content:center;
575    background:rgba(255,255,255,0.95);
576    backdrop-filter:blur(12px);
577    -webkit-backdrop-filter:blur(12px);
578    border:1px solid rgba(228,231,235,0.6);
579    box-shadow:0 2px 12px -3px rgba(0,0,0,0.12);
580  }
581  
582  .navbar #userMenu .login-btn.logged-in .login-text { 
583    display:none; 
584  }
585  
586  .navbar #userMenu .login-btn.logged-in .login-ico {
587    display:inline;
588    font-size:20px;
589    line-height:1;
590  }
591  
592  /* User dropdown positioning for mobile */
593  .navbar #userMenu .user-dropdown {
594    position:fixed !important;
595    top:60px !important;
596    right:16px !important;
597    left:auto !important;
598    min-width:200px;
599    background:rgba(255,255,255,0.98);
600    backdrop-filter:blur(20px);
601    -webkit-backdrop-filter:blur(20px);
602    border:1px solid rgba(228,231,235,0.6);
603    box-shadow:0 8px 24px -8px rgba(0,0,0,0.2);
604    z-index:1002 !important;
605    border-radius:12px;
606    padding:8px 0;
607  }
608  
609  /* Force visibility when not hidden attribute */
610  .navbar #userMenu .user-dropdown:not([hidden]) {
611    display:block !important;
612    visibility:visible !important;
613    opacity:1 !important;
614  }
615
616  /* Container untuk kategori dan tombol kategori sejajar */
617  .navbar .container:last-child {
618    display:flex !important;
619    align-items:center !important;
620    padding:4px 16px 6px !important;
621    gap:5px;
622    overflow:visible;
623  }
624  
625  /* Wrapper untuk mega-cats dengan fade gradient (mobile only) */
626  .mega-cats-wrapper {
627    position:relative;
628    flex:1;
629    overflow:hidden;
630    min-height:34px; /* Ensure content is visible */
631    display:flex; /* Ensure wrapper is flex container */
632  }
633  
634  /* Fade gradient indicator di ujung kanan (mobile only) */
635  .mega-cats-fade {
636    position:absolute;
637    top:0;
638    right:0;
639    bottom:0;
640    width:80px; /* Increased from 60px for better visibility */
641    background:linear-gradient(to left, 
642      rgba(255,251,235,1) 0%, 
643      rgba(255,251,235,0.98) 15%,
644      rgba(255,251,235,0.85) 35%, 
645      transparent 100%);
646    pointer-events:none;
647    z-index:5; /* Increased z-index */
648    transition:opacity 0.3s ease;
649  }
650  
651  /* Kategori menu di mobile */
652  .mega-cats.container {
653    display:flex !important;
654    gap:12px;
655    overflow-x:auto;
656    overflow-y:visible;
657    -webkit-overflow-scrolling:touch;
658    scrollbar-width:none;
659    flex:1;
660    padding:0 !important;
661    margin:0 !important;
662    position:relative; /* For z-index stacking */
663    z-index:1; /* Below fade gradient */
664  }
665  .mega-cats.container::-webkit-scrollbar { display:none; }
666
667  /* Styling untuk menu items di mobile */
668  .mega-cats a {
669    font-size:13px;
670    padding:6px 10px;
671    white-space:nowrap;
672    flex-shrink:0;
673    position:relative; /* Ensure proper stacking */
674  }
675  .mega-cats a.active {
676    background:var(--brand-600);
677    color:#fff;
678    font-weight:600;
679    border-radius:6px;
680  }
681  
682  /* Tombol kategori di mobile */
683  .mm-wp {
684    flex-shrink:0;
685  }
686  .mm2-trigger {
687    display:flex;
688    align-items:center;
689    gap:6px;
690    padding:6px 12px;
691    background:var(--gray-100);
692    color:var(--gray-700);
693    text-decoration:none;
694    border-radius:6px;
695    font-size:13px;
696    white-space:nowrap;
697  }
698  
699  .mm2-trigger .label-cat {display: none; }
700  /* Tombol more kategori */
701  .mega-more-btn {
702    border:1px solid var(--border);
703    background:#fff;
704    border-radius:10px;
705    padding:6px 14px;
706    font-size:18px;
707    cursor:pointer;
708  }
709  .mega-cats-more.active > .mega-more-btn {
710    background:var(--brand-600);
711    color:#fff;
712    border-color:var(--brand-600);
713  }
714  .mega-more-dropdown {
715    top:calc(100% + 10px);
716    right:0;
717    min-width:85vw;
718    max-width:90vw;
719    border-radius:16px;
720  }
721  .mega-more-grid {
722    grid-template-columns:repeat(auto-fill,minmax(120px,1fr));
723  }
724  
725  /* ==============================================================
726     MOBILE USER BOTTOM SHEET
727     ============================================================== */
728  .mobile-user-sheet {
729    position:fixed;
730    top:0;
731    left:0;
732    right:0;
733    bottom:0;
734    z-index:10000;
735    pointer-events:none;
736  }
737  
738  .mobile-user-sheet.active {
739    pointer-events:auto;
740  }
741  
742  .mobile-user-sheet-backdrop {
743    position:absolute;
744    top:0;
745    left:0;
746    right:0;
747    bottom:0;
748    background:rgba(0,0,0,0.5);
749    opacity:0;
750    transition:opacity .3s ease;
751  }
752  
753  .mobile-user-sheet.active .mobile-user-sheet-backdrop {
754    opacity:1;
755  }
756  
757  .mobile-user-sheet-content {
758    position:absolute;
759    bottom:0;
760    left:0;
761    right:0;
762    background:#fff;
763    border-radius:20px 20px 0 0;
764    box-shadow:0 -4px 24px rgba(0,0,0,0.15);
765    transform:translateY(100%);
766    transition:transform .3s cubic-bezier(0.4, 0.0, 0.2, 1);
767    padding-bottom:calc(68px + env(safe-area-inset-bottom));
768  }
769  
770  .mobile-user-sheet.active .mobile-user-sheet-content {
771    transform:translateY(0);
772  }
773  
774  .mobile-user-sheet-header {
775    padding:16px 20px 12px;
776    border-bottom:1px solid #e5e7eb;
777  }
778  
779  .mobile-user-sheet-handle {
780    width:40px;
781    height:4px;
782    background:#d1d5db;
783    border-radius:2px;
784    margin:0 auto 16px;
785  }
786  
787  .mobile-user-info {
788    display:flex;
789    align-items:center;
790    gap:12px;
791  }
792  
793  .mobile-user-avatar {
794    width:48px;
795    height:48px;
796    border-radius:50%;
797    background:linear-gradient(135deg, #FEF3C7, #FDE68A);
798    display:flex;
799    align-items:center;
800    justify-content:center;
801    font-size:24px;
802    flex-shrink:0;
803  }
804  
805  .mobile-user-details {
806    flex:1;
807    min-width:0;
808  }
809  
810  .mobile-user-name {
811    font-size:16px;
812    font-weight:600;
813    color:#111827;
814    margin-bottom:2px;
815    white-space:nowrap;
816    overflow:hidden;
817    text-overflow:ellipsis;
818  }
819  
820  .mobile-user-email {
821    font-size:13px;
822    color:#6b7280;
823    white-space:nowrap;
824    overflow:hidden;
825    text-overflow:ellipsis;
826  }
827  
828  .mobile-user-sheet-body {
829    padding:8px 0;
830  }
831  
832  .mobile-user-sheet-item {
833    display:flex;
834    align-items:center;
835    gap:16px;
836    padding:14px 20px;
837    text-decoration:none;
838    color:#374151;
839    font-size:15px;
840    transition:background .2s;
841    border:none;
842    background:none;
843    width:100%;
844    text-align:left;
845    cursor:pointer;
846  }
847  
848  .mobile-user-sheet-item:active {
849    background:#f3f4f6;
850  }
851  
852  .mobile-user-sheet-icon {
853    font-size:20px;
854    width:24px;
855    text-align:center;
856    flex-shrink:0;
857  }
858  
859  .mobile-user-sheet-label {
860    flex:1;
861    font-weight:500;
862  }
863  
864  .mobile-user-sheet-divider {
865    height:1px;
866    background:#e5e7eb;
867    margin:8px 0;
868  }
869}
870
871/* END NAVBAR */
872
873.logo img { display:block; }
874.search {
875  flex:1;
876  position:relative;
877  display:flex;
878  align-items:center;
879  gap:0;
880  background:#fff;
881  border:1.5px solid #e5e7eb;
882  border-radius:12px;
883  padding:0 16px;
884  height:38px;
885  transition:all 0.3s ease;
886}
887.search:focus-within {
888  border-color:#FBBF24;
889  box-shadow:0 0 0 3px rgba(251, 191, 36, 0.1);
890}
891.search input {
892  flex:1;
893  border:none;
894  outline:none;
895  background:transparent;
896  font-size:15px;
897  padding:0 12px;
898  height:100%;
899  color:#1f2937;
900}
901.search input::placeholder {
902  color:#9ca3af;
903}
904.search-icon-btn {
905  background:transparent;
906  border:none;
907  padding:0;
908  display:flex;
909  align-items:center;
910  justify-content:center;
911  cursor:pointer;
912  color:#6b7280;
913  transition:color 0.2s ease;
914  flex-shrink:0;
915}
916.search-icon-btn:hover {
917  color:#FBBF24;
918}
919.search-suggest {
920  position:absolute;
921  left:0; right:0; top:100%;
922  background:#fff;
923  border:1px solid var(--border);
924  border-radius:var(--radius);
925  padding:8px 0;
926  box-shadow:0 8px 20px rgba(0,0,0,.08);
927  margin-top:4px;
928  font-size:14px;
929  max-height:260px;
930  overflow:auto;
931}
932.search-suggest div {
933  padding:6px 12px;
934  cursor:pointer;
935}
936.search-suggest div:hover { background:var(--gray-100); }
937
938.nav-actions { display:flex; gap:10px; align-items:center; }
939.icon-btn, .login-btn {
940  background:linear-gradient(135deg, rgba(255,255,255,0.95), rgba(249,250,251,0.9));
941  border:1px solid rgba(228,231,235,0.6);
942  padding:8px 12px;
943  border-radius:var(--radius);
944  cursor:pointer;
945  font-size:14px;
946  transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
947  height: 38px;
948  position:relative;
949  box-shadow:
950    0 1px 3px rgba(0,0,0,0.06),
951    0 0 0 1px rgba(255,255,255,0.5) inset;
952  backdrop-filter:blur(8px);
953  -webkit-backdrop-filter:blur(8px);
954}
955.icon-btn:hover, .login-btn:hover { 
956  background:linear-gradient(135deg, rgba(249,250,251,1), rgba(243,244,246,0.95));
957  border-color:rgba(209,213,219,0.8);
958  box-shadow:
959    0 2px 8px rgba(0,0,0,0.08),
960    0 0 0 1px rgba(255,255,255,0.6) inset;
961  transform:translateY(-1px);
962}
963.cart-indicator, .compare-indicator { position:relative; }
964.badge {
965  background:linear-gradient(135deg, #EF4444, #DC2626);
966  color:#fff;
967  font-size:10.5px;
968  font-weight:600;
969  padding:2px 6px;
970  border-radius:999px;
971  position:absolute;
972  top:-6px; 
973  right:-6px;
974  min-width:18px;
975  height:18px;
976  display:flex;
977  align-items:center;
978  justify-content:center;
979  box-shadow:
980    0 2px 8px rgba(239,68,68,0.4),
981    0 0 0 2px #fff,
982    0 0 12px -2px rgba(239,68,68,0.6);
983  transition:all 0.3s ease;
984}
985.icon-btn:hover .badge {
986  transform:scale(1.08);
987  box-shadow:
988    0 4px 12px rgba(239,68,68,0.5),
989    0 0 0 2px #fff,
990    0 0 16px -2px rgba(239,68,68,0.7);
991}
992.badge.is-empty { display:none; }
993
994/* User Menu Dropdown */
995.user-menu {
996  position:relative;
997}
998.user-dropdown {
999  position:absolute;
1000  top:calc(100% + 8px);
1001  right:0;
1002  background:#fff;
1003  border:1px solid var(--border);
1004  border-radius:12px;
1005  box-shadow:0 10px 40px rgba(0,0,0,0.12);
1006  min-width:200px;
1007  padding:8px 0;
1008  z-index:9999;
1009}
1010
1011/* Ensure dropdown is visible when not hidden */
1012.user-dropdown:not([hidden]) {
1013  display:block !important;
1014  visibility:visible !important;
1015  opacity:1 !important;
1016}
1017
1018/* Mobile: Force user dropdown to be visible above everything */
1019@media (max-width:640px) {
1020  .user-dropdown:not([hidden]) {
1021    position:fixed !important;
1022    z-index:9999 !important;
1023    top:60px !important;
1024    right:16px !important;
1025  }
1026}
1027
1028.dropdown-item {
1029  display:flex;
1030  align-items:center;
1031  gap:12px;
1032  padding:10px 16px;
1033  text-decoration:none;
1034  color:var(--gray-700);
1035  font-size:14px;
1036  transition:var(--transition);
1037  border:none;
1038  background:none;
1039  width:100%;
1040  text-align:left;
1041  cursor:pointer;
1042}
1043.dropdown-item:hover {
1044  background:var(--gray-100);
1045  color:var(--gray-900);
1046}
1047.dropdown-icon {
1048  font-size:16px;
1049  opacity:0.7;
1050}
1051.dropdown-separator {
1052  height:1px;
1053  background:var(--border);
1054  margin:8px 0;
1055}
1056.login-btn.logged-in {
1057  background:linear-gradient(135deg, rgba(255,251,235,0.8), rgba(219,234,254,0.6));
1058  border:1px solid rgba(201,162,39,0.2);
1059  color:var(--brand-700);
1060  max-width:200px; /* Prevent button from getting too wide */
1061  display:inline-flex;
1062  align-items:center;
1063  gap:10px;
1064  padding:4px 12px 4px 4px;
1065  transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1066  position:relative;
1067  overflow:hidden;
1068  box-shadow:
1069    0 1px 3px rgba(201,162,39,0.08),
1070    0 0 0 1px rgba(255,255,255,0.5) inset,
1071    0 0 12px -6px rgba(201,162,39,0.1);
1072  backdrop-filter:blur(8px);
1073  -webkit-backdrop-filter:blur(8px);
1074}
1075/* Subtle background glow effect */
1076.login-btn.logged-in::before {
1077  content:'';
1078  position:absolute;
1079  top:50%;
1080  left:0;
1081  width:32px;
1082  height:32px;
1083  background:radial-gradient(circle, rgba(201,162,39,0.15), transparent 70%);
1084  transform:translateY(-50%);
1085  opacity:0;
1086  transition:opacity 0.3s ease;
1087}
1088.login-btn.logged-in:hover::before {
1089  opacity:1;
1090}
1091.login-btn.logged-in .user-avatar {
1092  width:32px;
1093  height:32px;
1094  border-radius:50%;
1095  background:linear-gradient(135deg, #FEF3C7 0%, #FDE68A 50%, #FCD34D 100%);
1096  color:#8B6914;
1097  font-size:11.5px;
1098  font-weight:700;
1099  display:flex;
1100  align-items:center;
1101  justify-content:center;
1102  flex-shrink:0;
1103  letter-spacing:0.3px;
1104  position:relative;
1105  box-shadow:
1106    0 2px 8px rgba(201,162,39,0.25),
1107    0 1px 3px rgba(0,0,0,0.12),
1108    inset 0 1px 0 rgba(255,255,255,0.5);
1109  transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1110}
1111/* Ring gradient border */
1112.login-btn.logged-in .user-avatar::before {
1113  content:'';
1114  position:absolute;
1115  inset:-2px;
1116  border-radius:50%;
1117  padding:2px;
1118  background:linear-gradient(135deg, #C9A227, #FDE68A, #C9A227);
1119  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
1120  -webkit-mask-composite:xor;
1121  mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
1122  mask-composite:exclude;
1123  opacity:0;
1124  transition:opacity 0.3s ease;
1125}
1126.login-btn.logged-in:hover .user-avatar::before {
1127  opacity:1;
1128}
1129.login-btn.logged-in:hover .user-avatar {
1130  transform:scale(1.08) rotate(-3deg);
1131  box-shadow:
1132    0 4px 12px rgba(201,162,39,0.35),
1133    0 2px 6px rgba(0,0,0,0.15),
1134    inset 0 1px 0 rgba(255,255,255,0.6);
1135}
1136.login-btn.logged-in .login-text {
1137  overflow:hidden;
1138  text-overflow:ellipsis;
1139  white-space:nowrap;
1140  flex:1;
1141  min-width:0;
1142  font-weight:500;
1143  font-size:14px;
1144  transition:color 0.2s ease;
1145}
1146.login-btn.logged-in:hover .login-text {
1147  color:var(--brand-600);
1148}
1149.login-btn.logged-in:hover {
1150  background:linear-gradient(135deg, rgba(255,251,235,0.95), rgba(219,234,254,0.85));
1151  border-color:rgba(201,162,39,0.3);
1152  box-shadow:
1153    0 4px 16px -4px rgba(201,162,39,0.25),
1154    0 0 0 1px rgba(201,162,39,0.1) inset,
1155    0 1px 2px rgba(0,0,0,0.05);
1156  transform:translateY(-1px);
1157}
1158
1159/* Desktop: hide wrapper elements, show menu normally */
1160@media (min-width: 761px) {
1161  .mega-cats-wrapper {
1162    display:contents; /* Remove wrapper from layout */
1163  }
1164  .mega-cats-fade {
1165    display:none !important; /* Hide fade on desktop */
1166  }
1167}
1168
1169.mega-cats {
1170  display:flex;
1171  gap:5px;
1172  /* padding:6px 16px 10px; */
1173  scrollbar-width:none;
1174  position: relative;
1175  z-index: 510;
1176  overflow-x:auto;
1177  -webkit-overflow-scrolling:touch;
1178  flex:1;
1179}
1180.mega-cats::-webkit-scrollbar { 
1181  display:none; 
1182}
1183.mega-cats a {
1184  text-decoration:none;
1185  font-size:14px;
1186  color:var(--gray-700);
1187  padding:6px 10px;
1188  border-radius:6px;
1189  white-space:nowrap;
1190  transition:var(--transition);
1191}
1192.mega-cats a:hover { background:var(--brand-100); color:var(--brand-600); }
1193/*.mega-cats::after {
1194  content:'';
1195  position:absolute;
1196  right:0;
1197  top:0;
1198  width:40px;
1199  height:100%;
1200  background:linear-gradient(to right,rgba(255,255,255,0),#fff);
1201  pointer-events:none;
1202}*/
1203
1204/* Tambahan untuk tombol More & dropdown.
1205   Style dasar .mega-cats dan .mega-cats a tetap pakai definisi Anda. */
1206
1207.mega-cats-more {
1208  position:relative;
1209  display:flex;
1210  align-items:center;
1211}
1212
1213.mega-cats-more.active > .mega-more-btn {
1214  background:var(--brand-600);
1215  color:#fff;
1216}
1217
1218.mega-more-btn {
1219  cursor:pointer;
1220  border:none;
1221  background:var(--gray-100,#f5f5f5);
1222  color:var(--gray-700,#333);
1223  padding:6px 12px;
1224  font-size:16px;
1225  line-height:1;
1226  border-radius:6px;
1227  display:flex;
1228  align-items:center;
1229  justify-content:center;
1230  transition:var(--transition);
1231}
1232.mega-more-btn:hover {
1233  background:var(--brand-100);
1234  color:var(--brand-600);
1235}
1236
1237.mega-more-dropdown {
1238  position:absolute;
1239  top:100%;
1240  right:0;
1241  margin-top:8px;
1242  background:#fff;
1243  border:1px solid var(--gray-200,#e3e6e9);
1244  border-radius:10px;
1245  padding:12px 14px 14px;
1246  min-width:400px;
1247  max-width:520px;
1248  max-height:420px;
1249  box-shadow:0 12px 32px -8px rgba(25,40,65,.25);
1250  z-index: 3000; 
1251}
1252
1253.mega-more-grid {
1254  display:grid;
1255  grid-template-columns:repeat(auto-fill,minmax(120px,1fr));
1256  gap:8px 10px;
1257  overflow-x: hidden;
1258  max-height:380px;
1259  overscroll-behavior:contain;
1260  scrollbar-width:thin;
1261}
1262.mega-more-grid::-webkit-scrollbar {
1263  width:6px;
1264}
1265.mega-more-grid::-webkit-scrollbar-thumb {
1266  background:var(--gray-300,#c7ccd1);
1267  border-radius:3px;
1268}
1269
1270/* Link di dropdown mewarisi style .mega-cats a (warna, padding, radius).
1271   Kita tambahkan state active agar konsisten. */
1272.mega-more-grid a.active {
1273  background:var(--brand-600);
1274  color:#fff;
1275  font-weight:600;
1276}
1277.hero {
1278  background:linear-gradient(135deg,var(--brand-50), #fff);
1279}
1280.hero-inner {
1281  display:grid;
1282  grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
1283  gap:48px;
1284  align-items:center;
1285}
1286.hero-text h1 {
1287  font-size:48px;
1288  line-height:1.1;
1289  margin:0;
1290  font-weight:600;
1291  font-family:"Poppins",sans-serif;
1292}
1293.hero-text .text-brand { color:var(--brand-500); }
1294.lead { font-size:18px; color:var(--gray-700); margin-top:16px; }
1295.hero-cta { margin-top:28px; display:flex; gap:16px; }
1296.hero-bullets { margin-top:28px; display:flex; gap:28px; font-size:14px; color:var(--gray-600); }
1297.hero-box {
1298  background:#fff;
1299  padding:10px;
1300  border-radius:16px;
1301  box-shadow:0 4px 18px rgba(0,0,0,.08);
1302  /* min-height:320px; */
1303  display:flex;
1304  align-items:center;
1305  justify-content:center;
1306}
1307.hero-box img {border-radius: 10px; }
1308
1309@media (max-width:680px) {
1310    .hero-inner { gap: 25px; }    
1311}
1312
1313.usp-section {
1314  position:relative;
1315  background:
1316    linear-gradient(180deg,#F8FAFC 0%, #FFFFFF 60%) ;
1317  border-top:1px solid var(--border);
1318  border-bottom:1px solid var(--border);
1319  padding:10px 0 10px;
1320}
1321
1322.usp-list {
1323  list-style:none;
1324  margin:0;
1325  padding:0;
1326  display:grid;
1327  gap:26px;
1328}
1329
1330.usp-list.variant-soft {
1331  grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
1332}
1333
1334.usp-item {
1335  display:flex;
1336  gap:16px;
1337  align-items:flex-start;
1338  background:#fff;
1339  padding:18px 18px 18px 16px;
1340  border:1px solid rgba(226,226,226,0.7);
1341  border-radius:18px;
1342  position:relative;
1343  overflow:hidden;
1344  isolation:isolate;
1345  box-shadow:0 2px 4px -1px rgba(0,0,0,.04), 0 6px 18px -6px rgba(0,0,0,.08);
1346  transition: .35s cubic-bezier(.16,.8,.32,1);
1347}
1348
1349.usp-item::before {
1350  content:"";
1351  position:absolute;
1352  inset:0;
1353  background:radial-gradient(circle at 18% 22%, rgba(201,162,39,.18), transparent 60%);
1354  opacity:.65;
1355  transition:.5s;
1356  z-index:-1;
1357}
1358
1359.usp-item:hover {
1360  transform:translateY(-6px);
1361  box-shadow:0 10px 26px -8px rgba(0,0,0,.15), 0 3px 8px -2px rgba(0,0,0,.08);
1362  border-color:rgba(201,162,39,.35);
1363}
1364
1365.usp-item:hover::before {
1366  background:radial-gradient(circle at 70% 40%, rgba(201,162,39,.22), transparent 65%);
1367}
1368
1369.usp-icon {
1370  flex:0 0 56px;
1371  width:56px;
1372  height:56px;
1373  border-radius:16px;
1374  display:flex;
1375  align-items:center;
1376  justify-content:center;
1377  background:
1378    linear-gradient(135deg,#FEF3C7,#FDE68A);
1379  position:relative;
1380  box-shadow:inset 0 0 0 1px rgba(255,255,255,.6), 0 4px 10px -3px rgba(0,0,0,.15);
1381  transition:.45s;
1382}
1383
1384.usp-item:hover .usp-icon {
1385  transform:rotate(-4deg) scale(1.06);
1386  box-shadow:inset 0 0 0 1px rgba(255,255,255,.8), 0 6px 14px -4px rgba(0,0,0,.20);
1387}
1388
1389.usp-icon svg {
1390  width:28px;
1391  height:28px;
1392  display:block;
1393  filter:drop-shadow(0 2px 2px rgba(0,0,0,.18));
1394}
1395
1396.usp-text h3 {
1397  font-size:15px;
1398  font-weight:600;
1399  margin:2px 0 6px;
1400  letter-spacing:.2px;
1401}
1402
1403.usp-text p {
1404  font-size:13px;
1405  line-height:1.3;
1406  margin:0;
1407  color:var(--gray-600);
1408}
1409
1410@media (max-width:680px) {
1411  .usp-section { padding: 5px 0 5px; margin-bottom: 0.75rem;}
1412  .usp-item { padding:16px 14px; }
1413  .usp-icon { width:50px;height:50px;border-radius:14px; }
1414  .usp-text h3 { font-size:14px; margin-bottom:4px; }
1415  .usp-text p { font-size:12.5px; }
1416  .usp-list { gap: 10px; }
1417}
1418
1419.flash { padding:50px 0 30px; background:#fff; }
1420.flash-head {
1421  display:flex; align-items:center;
1422  justify-content:space-between;
1423  margin-bottom:20px;
1424}
1425.flash-head h2 { margin:0; font-size:24px; font-family:"Poppins"; }
1426.flash .countdown {
1427  font-family:monospace;
1428  background:#111;
1429  color:#fff;
1430  padding:8px 14px;
1431  border-radius:8px;
1432  font-size:16px;
1433  letter-spacing:1px;
1434}
1435.flash-scroll {
1436  display:flex;
1437  gap:16px;
1438  overflow-x:auto;
1439  padding-bottom:6px;
1440  scrollbar-width:none;
1441}
1442.flash-scroll::-webkit-scrollbar { display:none; }
1443
1444.products { padding:60px 0; background:#fff; }
1445.products-head {
1446  display:flex; justify-content:space-between; align-items:center;
1447  margin-bottom:24px;
1448  flex-wrap:wrap;
1449  gap:16px;
1450}
1451.products-head h2 { margin:0; font-size:24px; font-family:"Poppins"; }
1452.product-filters input {
1453  padding:8px 12px;
1454  border:1px solid var(--border);
1455  border-radius:8px;
1456  font-size:14px;
1457  width:220px;
1458}
1459
1460.product-grid {
1461  display:grid;
1462  gap:24px;
1463  grid-template-columns:repeat(auto-fill,minmax(180px,1fr));
1464}
1465
1466.prod-card {
1467  background:#fff;
1468  border:1px solid var(--border);
1469  padding:12px;
1470  border-radius:12px;
1471  position:relative;
1472  display:flex;
1473  flex-direction:column;
1474  transition:var(--transition);
1475}
1476.prod-card.small { min-width:160px; }
1477.prod-card:hover { box-shadow:0 8px 20px rgba(0,0,0,.12); transform:translateY(-3px); }
1478.prod-card .img-box {
1479  background:var(--gray-100);
1480  border-radius:10px;
1481  aspect-ratio:1/1;
1482  display:flex;
1483  align-items:center;
1484  justify-content:center;
1485  overflow:hidden;
1486}
1487.prod-card .img-box img { width:100%; height:100%; object-fit:contain; transition:var(--transition); }
1488.prod-card:hover .img-box img { transform:scale(1.04); }
1489.prod-card .name {
1490  font-size:13.5px;
1491  font-weight:500;
1492  margin:10px 0 6px;
1493  min-height:36px;
1494  line-height:1.3;
1495}
1496.price {
1497  display:flex;
1498  gap:6px;
1499  align-items:center;
1500  flex-wrap:wrap;
1501}
1502.price strong { color:var(--brand-600); font-size:14px; }
1503.price .old {
1504  font-size:11px;
1505  color:var(--gray-500);
1506  text-decoration:line-through;
1507}
1508.tag.discount {
1509  position:absolute;
1510  top:8px; left:8px;
1511  background:#E54848;
1512  color:#fff;
1513  font-size:11px;
1514  padding:4px 6px;
1515  border-radius:6px;
1516  font-weight:600;
1517}
1518.btn {
1519  border:none;
1520  cursor:pointer;
1521  font-weight:500;
1522  font-size:14px;
1523  padding:12px 22px;
1524  border-radius:10px;
1525  background:var(--gray-900);
1526  color:#fff;
1527  transition:var(--transition);
1528  text-decoration:none;
1529  text-align:center;
1530  display:inline-flex;
1531  align-items:center;
1532  justify-content:center;
1533  gap:6px;
1534}
1535.btn:hover { background:#000; }
1536.btn-primary { background:var(--brand-500); }
1537.btn-primary:hover { background:var(--brand-600); }
1538.btn-outline {
1539  background:#fff;
1540  color:var(--gray-900);
1541  border:1px solid var(--border);
1542}
1543.btn-outline:hover { background:var(--gray-100); }
1544.btn-sm { padding:8px 14px; font-size:13px; }
1545.btn-mini { padding:6px 10px; font-size:12px; }
1546.add-cart { margin-top:auto; background:var(--brand-500); color:#fff; }
1547.add-cart:hover { background:var(--brand-600); }
1548
1549/* ==== FOOTER COMPACT FIX PATCH (final) ==== */
1550
1551/* 1. Pastikan grid aktif */
1552
1553.site-footer.footer-compact .ft-grid {
1554  display:grid;
1555  grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
1556  gap:30px 40px;
1557  align-items:start;
1558}
1559
1560/* 2. Spacing atas bawah dipadatkan */
1561.site-footer.footer-compact .ft-top {
1562  padding:42px 0 26px;
1563}
1564
1565/* 3. Judul & teks */
1566.site-footer.footer-compact .ft-title {
1567  margin:0 0 14px;
1568  font-size:14px;
1569  letter-spacing:.8px;
1570  text-transform:uppercase;
1571  font-weight:600;
1572  color:#82b1c9;
1573}
1574
1575.site-footer.footer-compact .ft-desc,
1576.site-footer.footer-compact .ft-text {
1577  margin:0 0 16px;
1578  font-size:13.5px;
1579  line-height:1.45;
1580  color:#c6d6e0;
1581  max-width:330px;
1582}
1583.site-footer.footer-compact .ft-desc.sm-gap { margin-top:18px; }
1584
1585/* 4. Reset ikon SVG agar tidak membesar */
1586.site-footer.footer-compact svg {
1587  width:20px;
1588  height:20px;
1589  flex:0 0 20px;
1590  display:inline-block;
1591}
1592.site-footer.footer-compact .ft-social svg { width:18px; height:18px; }
1593.site-footer.footer-compact .ft-wa-btn svg { width:18px; height:18px; }
1594
1595/* Jika ada aturan global svg { width:100%; } — paksa override */
1596svg { max-width:100%; }
1597.site-footer.footer-compact svg {
1598  width:20px !important;
1599  height:20px !important;
1600}
1601
1602/* 5. Link list */
1603.site-footer.footer-compact .ft-links {
1604  list-style:none;
1605  margin:0 0 12px;
1606  padding:0;
1607  display:flex;
1608  flex-direction:column;
1609  gap:4px;
1610}
1611.site-footer.footer-compact .ft-links li { margin:0; }
1612.site-footer.footer-compact .ft-links a {
1613  display:flex;
1614  align-items:center;
1615  gap:8px;
1616  font-size:13.5px;
1617  padding:5px 2px;
1618  text-decoration:none;
1619  color:#c6d6e0;
1620  border-radius:8px;
1621  transition:.25s;
1622}
1623.site-footer.footer-compact .ft-links a:hover {
1624  background:rgba(255,255,255,.06);
1625  padding-left:6px;
1626  color:#fff;
1627}
1628
1629.site-footer.footer-compact .ico-s {
1630  width:24px;
1631  height:24px;
1632  background:#103346;
1633  color:#89b8cf;
1634  border-radius:6px;
1635  display:flex;
1636  align-items:center;
1637  justify-content:center;
1638  flex:0 0 24px;
1639  font-size:12px;
1640  transition:.3s;
1641}
1642.site-footer.footer-compact .ft-links a:hover .ico-s {
1643  background:#C9A227;
1644  color:#fff;
1645  transform:rotate(-4deg);
1646}
1647
1648/* 6. WhatsApp button */
1649.site-footer.footer-compact .ft-wa-btn {
1650  display:inline-flex;
1651  align-items:center;
1652  gap:8px;
1653  background:#fff;
1654  color:#062437;
1655  padding:8px 18px;
1656  font-size:13.5px;
1657  border-radius:22px;
1658  font-weight:500;
1659  text-decoration:none;
1660  box-shadow:0 3px 8px -3px rgba(0,0,0,.45);
1661  transition:.25s;
1662}
1663.site-footer.footer-compact .ft-wa-btn:hover {
1664  transform:translateY(-2px);
1665  box-shadow:0 6px 14px -6px rgba(0,0,0,.55);
1666}
1667
1668/* 7. Sosial */
1669.site-footer.footer-compact .ft-social {
1670  display:flex;
1671  gap:12px;
1672  margin-top:4px;
1673}
1674.site-footer.footer-compact .ft-social a {
1675  width:34px;
1676  height:34px;
1677  background:rgba(255,255,255,.08);
1678  display:flex;
1679  align-items:center;
1680  justify-content:center;
1681  border-radius:10px;
1682  color:#c6d6e0;
1683  transition:.25s;
1684}
1685.site-footer.footer-compact .ft-social a:hover {
1686  background:#C9A227;
1687  color:#fff;
1688}
1689
1690/* 8. Alamat */
1691.site-footer.footer-compact .ft-address {
1692  font-size:12.5px;
1693  line-height:1.44;
1694  color:#8fa4b1;
1695  max-width:330px;
1696  margin-top:12px;
1697}
1698.site-footer.footer-compact .ft-address .company {
1699  margin:0 0 4px;
1700  font-weight:600;
1701  color:#e5f2f7;
1702  font-size:12.8px;
1703}
1704
1705/* 9. Payments kecil & rapi */
1706.site-footer.footer-compact .ft-payments {
1707  margin-top:14px;
1708  display:flex;
1709  flex-direction:column;
1710  gap:8px;
1711  max-width:350px;
1712}
1713.site-footer.footer-compact .ft-payments .pay-row {
1714  display:flex;
1715  gap:8px;
1716  flex-wrap:wrap;
1717}
1718.site-footer.footer-compact .ft-payments img {
1719  width:46px;
1720  height:32px;
1721  padding:3px 5px;
1722  background:#fff;
1723  border-radius:7px;
1724  object-fit:contain;
1725  box-shadow:0 2px 6px -3px rgba(0,0,0,.45);
1726  transition:.25s;
1727}
1728.site-footer.footer-compact .ft-payments img:hover {
1729  transform:translateY(-3px);
1730  box-shadow:0 5px 12px -6px rgba(0,0,0,.55);
1731}
1732
1733/* 10. Bottom bar */
1734.site-footer.footer-compact .ft-bottom {
1735  border-top:1px solid rgba(255,255,255,.08);
1736  background:#041f31;
1737  padding:12px 0 16px;
1738  margin-top:4px;
1739}
1740.site-footer.footer-compact .ft-copy {
1741  font-size:11.5px;
1742  color:#8fa7b5;
1743  display:flex;
1744  flex-wrap:wrap;
1745  gap:12px;
1746  justify-content:center;
1747  line-height:1.3;
1748  margin:0;
1749}
1750.site-footer.footer-compact .ft-copy span {
1751  position:relative;
1752  padding:0 10px;
1753}
1754.site-footer.footer-compact .ft-copy span+span:before {
1755  content:"";
1756  position:absolute;
1757  left:0;
1758  top:50%;
1759  width:1px;
1760  height:12px;
1761  background:rgba(255,255,255,.14);
1762  transform:translateY(-50%);
1763}
1764@media (max-width:580px){
1765  .site-footer.footer-compact .ft-top { padding:34px 0 18px; }
1766  .site-footer.footer-compact .ft-grid { gap:26px 20px; }
1767  .site-footer.footer-compact .ft-copy span { padding:0; }
1768  .site-footer.footer-compact .ft-copy span+span:before { display:none; }
1769}
1770.copy {
1771  margin-top:40px;
1772  border-top:1px solid #222;
1773  text-align:center;
1774  padding:18px 0;
1775  font-size:12px;
1776  color:#666;
1777}
1778
1779@media (max-width:760px) {
1780  .hero-text h1 { font-size:38px; }
1781  .hero { padding:0px 0 30px; }
1782  .topbar-inner { flex-direction:column; gap:4px; padding:6px 0; }
1783  .nav-inner { flex-wrap:wrap; }
1784  .mega-cats { gap:10px; }
1785  .product-grid { gap:16px; }
1786  .prod-card { padding:10px; }
1787}
1788
1789/* === PLAZA IT DARK ORANGE FOOTER THEME === */
1790.site-footer.footer-compact {
1791  --ft-bg-0:#051722;
1792  --ft-bg-1:#072534;
1793  --ft-bg-2:#0B2F40;
1794  --ft-bg-3:#103A4E;
1795  --ft-gold:#C9A227;
1796  --ft-gold-dark:#8B6914;
1797  --ft-text:#E9F2F6;
1798  --ft-text-sec:#B8CAD3;
1799  --ft-text-muted:#8FA4B1;
1800  --ft-divider:rgba(255,255,255,0.08);
1801  --ft-glow:rgba(201,162,39,0.18);
1802  --ft-glow-fade:rgba(201,162,39,0);
1803  color:var(--ft-text);
1804}
1805
1806/* BASE TEXT / LINKS (override sebelumnya jika perlu) */
1807.site-footer.footer-compact .ft-title { color:#9CC5D7; }
1808.site-footer.footer-compact .ft-links a { color:var(--ft-text-sec); }
1809.site-footer.footer-compact .ft-links a:hover { color:#fff; }
1810.site-footer.footer-compact .ft-address { color:var(--ft-text-muted); }
1811.site-footer.footer-compact .ft-copy { color:#9AB2BD; }
1812
1813/* ICON BACKGROUND (kubu gelap) */
1814.site-footer.footer-compact .ico-s {
1815  background:#0F3647;
1816  color:#9FC7DA;
1817}
1818.site-footer.footer-compact .ft-links a:hover .ico-s {
1819  background:var(--ft-gold);
1820  color:#fff;
1821}
1822
1823/* WhatsApp button biarkan putih agar kontras */
1824.site-footer.footer-compact .ft-wa-btn {
1825  box-shadow:0 4px 12px -5px rgba(0,0,0,.6), 0 0 0 3px rgba(255,255,255,0.05);
1826}
1827.site-footer.footer-compact .ft-wa-btn:hover {
1828  box-shadow:0 8px 20px -8px rgba(0,0,0,.65), 0 0 0 3px rgba(201,162,39,0.25);
1829}
1830
1831/* Payment cards border halus */
1832.site-footer.footer-compact .ft-payments img {
1833  box-shadow:0 2px 6px -3px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,0.05);
1834}
1835.site-footer.footer-compact .ft-payments img:hover {
1836  box-shadow:0 6px 14px -6px rgba(0,0,0,.65), 0 0 0 1px rgba(201,162,39,0.4);
1837}
1838
1839/* ========== VARIANT A: DIAGONAL EMBER (AKTIF) ========== */
1840.site-footer.footer-compact.variant-ember {
1841  background:
1842    radial-gradient(circle at 78% 82%, var(--ft-glow) 0%, var(--ft-glow-fade) 55%),
1843    linear-gradient(140deg,var(--ft-bg-1) 0%, var(--ft-bg-2) 55%, var(--ft-bg-0) 100%);
1844}
1845.site-footer.footer-compact.variant-ember .ft-bottom {
1846  background:rgba(0,0,0,0.25);
1847  backdrop-filter:blur(2px);
1848}
1849
1850/* ========== VARIANT B: TOP BAR BLUE ========== */
1851.site-footer.footer-compact.variant-bar {
1852  background:var(--ft-bg-1);
1853  position:relative;
1854}
1855.site-footer.footer-compact.variant-bar::before {
1856  content:"";
1857  position:absolute;
1858  left:0;top:0;
1859  width:100%;height:3px;
1860  background:linear-gradient(90deg,var(--ft-gold-dark),var(--ft-gold));
1861  box-shadow:0 0 12px -2px var(--ft-gold);
1862}
1863.site-footer.footer-compact.variant-bar .ft-bottom {
1864  background:#041c28;
1865}
1866
1867/* ========== VARIANT C: RADIAL GLOW BAWAH ========== */
1868.site-footer.footer-compact.variant-radial {
1869  background:
1870    radial-gradient(circle at 70% 90%, rgba(201,162,39,0.22) 0%, rgba(201,162,39,0.04) 38%, rgba(201,162,39,0) 65%),
1871    linear-gradient(150deg,#052131 0%, #062d3f 50%, #041924 100%);
1872}
1873.site-footer.footer-compact.variant-radial .ft-bottom {
1874  background:#041c29;
1875}
1876
1877/* ========== VARIANT D: LAYERED STRIPES ========== */
1878.site-footer.footer-compact.variant-stripes {
1879  background:
1880    repeating-linear-gradient(180deg, rgba(255,255,255,0.015) 0 38px, rgba(255,255,255,0) 38px 76px),
1881    linear-gradient(130deg,#052636 0%, #062a3c 60%, #041d28 100%);
1882  position:relative;
1883}
1884.site-footer.footer-compact.variant-stripes::after {
1885  content:"";
1886  position:absolute;
1887  left:0;bottom:0;
1888  width:100%;height:110px;
1889  background:linear-gradient(0deg,rgba(0,0,0,.35),rgba(0,0,0,0));
1890  pointer-events:none;
1891}
1892.site-footer.footer-compact.variant-stripes .ft-bottom {
1893  background:#031821;
1894}
1895
1896/* Garis aksen biru tipis di atas bottom */
1897.site-footer.footer-compact.variant-stripes .ft-bottom {
1898  position:relative;
1899}
1900.site-footer.footer-compact.variant-stripes .ft-bottom::before {
1901  content:"";
1902  position:absolute;
1903  left:0;top:0;
1904  width:100%;height:2px;
1905  background:linear-gradient(90deg,rgba(201,162,39,0),rgba(201,162,39,.65),rgba(201,162,39,0));
1906}
1907
1908/* ========== VARIANT E: GLASS PANEL ========== */
1909.site-footer.footer-compact.variant-glass {
1910  background:linear-gradient(145deg,#292205 0%, #292205 65%, #292205 100%);
1911}
1912.site-footer.footer-compact.variant-glass .ft-grid {
1913  position:relative;
1914  padding:32px clamp(10px,2vw,24px);
1915  border-radius:22px;
1916  background:linear-gradient(140deg,rgba(255,255,255,0.06),rgba(255,255,255,0.02));
1917  border:1px solid rgba(255,255,255,0.08);
1918  box-shadow:0 12px 28px -12px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.04) inset;
1919  backdrop-filter:blur(8px);
1920  -webkit-backdrop-filter:blur(8px);
1921}
1922.site-footer.footer-compact.variant-glass .ft-bottom {
1923  background:#02141d;
1924}
1925
1926/* HOVER ACCENTS */
1927.site-footer.footer-compact a {
1928  transition:color .25s, background .25s, box-shadow .25s;
1929}
1930.site-footer.footer-compact .ft-links a:hover {
1931  box-shadow:0 2px 6px -3px rgba(0,0,0,.5),
1932             0 0 0 1px rgba(201,162,39,0.55);
1933}
1934.site-footer.footer-compact .ft-social a {
1935  background:#0E3445;
1936  color:#bcd4dd;
1937}
1938.site-footer.footer-compact .ft-social a:hover {
1939  background:var(--ft-gold);
1940  color:#fff;
1941  box-shadow:0 0 0 3px rgba(201,162,39,0.28);
1942}
1943
1944/* Divider di atas footer (opsional) */
1945.footer-divider-top {
1946  height:1px;
1947  background:linear-gradient(90deg,transparent,rgba(255,255,255,0.15),transparent);
1948  width:100%;
1949  margin:0 0 4px;
1950}
1951
1952/* ==== MOBILE FOOTER COLLAPSE (Non-Homepage Only) ==== */
1953
1954/* Desktop: wrapper tidak mengganggu grid layout */
1955.ft-collapsible-content {
1956  display: contents;
1957}
1958
1959@media (max-width: 768px) {
1960  /* Wrapper untuk konten yang bisa collapse */
1961  .ft-top.ft-collapsible .ft-collapsible-content {
1962    display: block;
1963    max-height: 80px;
1964    overflow: hidden;
1965    position: relative;
1966    transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
1967  }
1968  
1969  /* Fade gradient overlay */
1970  .ft-top.ft-collapsible .ft-collapsible-content::after {
1971    content: '';
1972    position: absolute;
1973    bottom: 0;
1974    left: 0;
1975    right: 0;
1976    height: 70px;
1977    background: linear-gradient(to bottom, transparent 0%, rgba(6, 43, 69, 0.6) 40%, #062b45 100%);
1978    pointer-events: none;
1979    opacity: 1;
1980    transition: opacity 0.3s ease;
1981    z-index: 1;
1982  }
1983  
1984  /* State: Expanded */
1985  .ft-top.ft-collapsible.expanded .ft-collapsible-content {
1986    max-height: 1500px;
1987  }
1988  
1989  .ft-top.ft-collapsible.expanded .ft-collapsible-content::after {
1990    opacity: 0;
1991  }
1992  
1993  /* Expand Button - Di luar collapsible, selalu visible */
1994  .ft-expand-btn {
1995    display: flex;
1996    align-items: center;
1997    justify-content: center;
1998    gap: 8px;
1999    width: 100%;
2000    margin: -8px 0 0;
2001    padding: 12px 20px;
2002    background: rgba(255,255,255,0.12);
2003    border: 1px solid rgba(255,255,255,0.25);
2004    border-radius: 10px;
2005    color: #e5f2f7;
2006    font-size: 13.5px;
2007    font-weight: 500;
2008    cursor: pointer;
2009    transition: all 0.3s ease;
2010    outline: none;
2011    position: relative;
2012    z-index: 10;
2013  }
2014  
2015  .ft-expand-btn:hover {
2016    background: rgba(255,255,255,0.18);
2017    border-color: rgba(255,255,255,0.35);
2018    color: #fff;
2019    transform: translateY(-2px);
2020  }
2021  
2022  .ft-expand-btn:active {
2023    transform: scale(0.98);
2024  }
2025  
2026  .ft-expand-btn svg {
2027    width: 18px;
2028    height: 18px;
2029    transition: transform 0.3s ease;
2030    fill: currentColor;
2031  }
2032  
2033  .ft-top.ft-collapsible.expanded .ft-expand-btn {
2034    margin-top: 15px;
2035  }
2036  
2037  .ft-top.ft-collapsible.expanded .ft-expand-btn svg {
2038    transform: rotate(180deg);
2039  }
2040}
2041
2042/* Desktop: hide expand button */
2043@media (min-width: 769px) {
2044  .ft-expand-btn {
2045    display: none !important;
2046  }
2047}
2048
2049/* RESPONSIVE (jika background panel butuh penyesuaian) */
2050@media (max-width:640px){
2051  .site-footer.footer-compact.variant-glass .ft-grid {
2052    padding:26px 18px;
2053  }
2054}
2055
2056</style>
2057  <style data-inlined="bottom-nav.css">
2058/* Mobile Bottom Navigation */
2059.bottom-nav {
2060  position: fixed;
2061  bottom: 0;
2062  left: 0;
2063  right: 0;
2064  background: #ffffff;
2065  border-top: 1px solid #e5e7eb;
2066  z-index: 9998; /* Below chat widget */
2067  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
2068  display: none; /* Hidden on desktop */
2069}
2070
2071.bottom-nav-inner {
2072  display: flex;
2073  justify-content: space-around;
2074  align-items: center;
2075  padding: 2px 0;
2076  max-width: 640px;
2077  margin: 0 auto;
2078}
2079
2080.bottom-nav-item {
2081  flex: 1;
2082  display: flex;
2083  flex-direction: column;
2084  align-items: center;
2085  justify-content: center;
2086  gap: 4px;
2087  padding: 8px 8px;
2088  background: none;
2089  border: none;
2090  cursor: pointer;
2091  text-decoration: none;
2092  color: #6b7280;
2093  transition: all 0.3s ease;
2094  position: relative;
2095}
2096
2097.bottom-nav-item:hover {
2098  color: #C9A227;
2099}
2100
2101.bottom-nav-item.active {
2102  flex-direction: row;
2103  gap: 8px;
2104  background: linear-gradient(135deg, #C9A227 0%, #8B6914 100%);
2105  color: white;
2106  border-radius: 20px;
2107  padding: 8px 16px;
2108  box-shadow: 0 2px 8px rgba(201, 162, 39, 0.3);
2109}
2110
2111.bottom-nav-item-icon {
2112  width: 24px;
2113  height: 24px;
2114  position: relative;
2115}
2116
2117.bottom-nav-item-icon svg {
2118  width: 100%;
2119  height: 100%;
2120}
2121
2122/* Icon variant switching */
2123.bottom-nav-item .icon-outlined {
2124  display: block;
2125}
2126
2127.bottom-nav-item .icon-filled {
2128  display: none;
2129}
2130
2131.bottom-nav-item.active .icon-outlined {
2132  display: none;
2133}
2134
2135.bottom-nav-item.active .icon-filled {
2136  display: block;
2137}
2138
2139.bottom-nav-item-label {
2140  font-size: 12px;
2141  font-weight: 600;
2142  line-height: 1;
2143  display: none;
2144  white-space: nowrap;
2145}
2146
2147.bottom-nav-item.active .bottom-nav-item-label {
2148  display: block;
2149}
2150
2151/* Avatar styles for bottom-nav */
2152.bottom-nav-avatar-img,
2153.bottom-nav-avatar-initials {
2154  width: 24px;
2155  height: 24px;
2156  border-radius: 50%;
2157  display: block;
2158}
2159
2160.bottom-nav-avatar-img {
2161  object-fit: cover;
2162}
2163
2164.bottom-nav-avatar-initials {
2165  background: linear-gradient(135deg, #C9A227 0%, #8B6914 100%);
2166  color: white;
2167  font-size: 10px;
2168  font-weight: 700;
2169  display: flex;
2170  align-items: center;
2171  justify-content: center;
2172  line-height: 1;
2173  letter-spacing: 0.5px;
2174}
2175
2176/* Hide SVG when avatar is shown */
2177.bottom-nav-item-icon:has(.bottom-nav-avatar-img) .bottom-nav-svg-icon,
2178.bottom-nav-item-icon:has(.bottom-nav-avatar-initials) .bottom-nav-svg-icon {
2179  display: none;
2180}
2181
2182/* Active state adjustments for avatar */
2183.bottom-nav-item.active .bottom-nav-avatar-initials {
2184  background: white;
2185  color: #C9A227;
2186  border: 2px solid rgba(255, 255, 255, 0.8);
2187  box-sizing: border-box;
2188}
2189
2190.bottom-nav-item.active .bottom-nav-avatar-img {
2191  border: 2px solid rgba(255, 255, 255, 0.9);
2192  box-sizing: border-box;
2193}
2194
2195.bottom-nav-item-badge {
2196  position: absolute;
2197  top: -4px;
2198  right: -4px;
2199  background: #ef4444;
2200  color: white;
2201  border-radius: 10px;
2202  padding: 2px 5px;
2203  font-size: 10px;
2204  font-weight: 600;
2205  min-width: 18px;
2206  height: 18px;
2207  display: flex;
2208  align-items: center;
2209  justify-content: center;
2210  line-height: 1;
2211}
2212
2213/* Show on mobile only */
2214@media (max-width: 640px) {
2215  .bottom-nav {
2216    display: block;
2217  }
2218  
2219  /* Add bottom padding to body to prevent content being hidden */
2220  body {
2221    padding-bottom: 60px;
2222  }
2223}
2224
2225/* Mobile User Sheet - Bottom Sheet for Profile Menu */
2226.mobile-user-sheet {
2227  position: fixed;
2228  bottom: 0;
2229  left: 0;
2230  right: 0;
2231  z-index: 10000;
2232  pointer-events: none;
2233}
2234
2235.mobile-user-sheet:not([hidden]) {
2236  pointer-events: auto;
2237}
2238
2239.mobile-user-sheet-backdrop {
2240  position: fixed;
2241  inset: 0;
2242  background: rgba(0, 0, 0, 0.5);
2243  opacity: 0;
2244  transition: opacity 0.2s ease-out;
2245}
2246
2247.mobile-user-sheet.active .mobile-user-sheet-backdrop {
2248  opacity: 1;
2249}
2250
2251.mobile-user-sheet-content {
2252  position: absolute;
2253  bottom: 0;
2254  left: 0;
2255  right: 0;
2256  background: white;
2257  border-radius: 16px 16px 0 0;
2258  max-height: 80vh;
2259  overflow-y: auto;
2260  transform: translateY(100%);
2261  transition: transform 0.3s ease-out;
2262}
2263
2264.mobile-user-sheet.active .mobile-user-sheet-content {
2265  transform: translateY(0);
2266}
2267
2268.mobile-user-sheet-handle {
2269  width: 40px;
2270  height: 4px;
2271  background: #d1d5db;
2272  border-radius: 2px;
2273  margin: 12px auto 16px;
2274}
2275
2276.mobile-user-sheet-header {
2277  padding: 0 20px 16px;
2278  border-bottom: 1px solid #e5e7eb;
2279}
2280
2281.mobile-user-info {
2282  display: flex;
2283  align-items: center;
2284  gap: 12px;
2285}
2286
2287.mobile-user-avatar {
2288  width: 48px;
2289  height: 48px;
2290  background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 50%, #FCD34D 100%);
2291  border-radius: 50%;
2292  display: flex;
2293  align-items: center;
2294  justify-content: center;
2295  font-size: 24px;
2296  color: #8B6914;
2297  font-weight: 600;
2298}
2299
2300.mobile-user-details {
2301  flex: 1;
2302}
2303
2304.mobile-user-name {
2305  font-weight: 600;
2306  font-size: 16px;
2307  color: #1f2937;
2308  margin-bottom: 4px;
2309}
2310
2311.mobile-user-email {
2312  font-size: 13px;
2313  color: #6b7280;
2314}
2315
2316.mobile-user-sheet-body {
2317  padding: 8px 0;
2318}
2319
2320.mobile-user-sheet-item {
2321  display: flex;
2322  align-items: center;
2323  gap: 12px;
2324  padding: 14px 20px;
2325  background: none;
2326  border: none;
2327  width: 100%;
2328  text-align: left;
2329  cursor: pointer;
2330  text-decoration: none;
2331  color: #374151;
2332  transition: background 0.2s;
2333}
2334
2335.mobile-user-sheet-item:hover {
2336  background: #f9fafb;
2337}
2338
2339.mobile-user-sheet-icon {
2340  font-size: 20px;
2341  width: 24px;
2342  text-align: center;
2343}
2344
2345.mobile-user-sheet-label {
2346  font-size: 15px;
2347  font-weight: 500;
2348}
2349
2350.mobile-user-sheet-divider {
2351  height: 1px;
2352  background: #e5e7eb;
2353  margin: 8px 0;
2354}
2355
2356@keyframes fadeIn {
2357  from {
2358    opacity: 0;
2359  }
2360  to {
2361    opacity: 1;
2362  }
2363}
2364
2365@keyframes slideUp {
2366  from {
2367    transform: translateY(100%);
2368  }
2369  to {
2370    transform: translateY(0);
2371  }
2372}
2373
2374/* Ensure chat widget works well with bottom nav */
2375@media (max-width: 640px) {
2376  .chat-widget {
2377    bottom: 0 !important; /* Chat opens from bottom */
2378  }
2379  
2380  .chat-popup {
2381    /* Chat popup should be above bottom nav */
2382    z-index: 9999;
2383  }
2384}
2385
2386</style>
2387  <style data-inlined="search-suggest.css">
2388.search { 
2389  position: relative; 
2390}
2391
2392/* AI Bot Toggle Button */
2393.ai-bot-toggle {
2394  display: flex;
2395  align-items: center;
2396  justify-content: center;
2397  background: transparent;
2398  border: none;
2399  padding: 8px;
2400  cursor: pointer;
2401  border-radius: 8px;
2402  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2403  margin-left: 4px;
2404  flex-shrink: 0;
2405  display: none;
2406}
2407
2408.ai-bot-toggle .bot-icon {
2409  color: #9ca3af;
2410  transition: all 0.3s ease;
2411}
2412
2413.ai-bot-toggle:hover .bot-icon {
2414  color: #6b7280;
2415  transform: scale(1.1);
2416}
2417
2418.ai-bot-toggle.active .bot-icon {
2419  color: #FBBF24;
2420  animation: botPulse 2s ease-in-out infinite;
2421}
2422
2423.ai-bot-toggle.active {
2424  background: rgba(251, 191, 36, 0.1);
2425}
2426
2427.ai-bot-toggle.active:hover .bot-icon {
2428  color: #CA8A04;
2429}
2430
2431@keyframes botPulse {
2432  0%, 100% { transform: scale(1); }
2433  50% { transform: scale(1.12); }
2434}
2435
2436/* Search Suggest Box */
2437.search-suggest {
2438  position: absolute;
2439  top: 100%; left: 0; right: 0;
2440  background: #fff;
2441  border: 1px solid #e1e4e8;
2442  border-top: none;
2443  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
2444  max-height: 400px;
2445  overflow-y: auto;
2446  z-index: 100;
2447  padding: 0;
2448  font-size: 14px;
2449  border-radius: 0 0 8px 8px;
2450}
2451
2452.suggest-item {
2453  display: flex;
2454  align-items: center;
2455  gap: 8px;
2456  padding: 10px 16px;
2457  cursor: pointer;
2458  transition: all 0.2s ease;
2459  border-bottom: 1px solid #f1f5f9;
2460}
2461
2462.suggest-item:last-child {
2463  border-bottom: none;
2464}
2465
2466.suggest-item:hover,
2467.suggest-item.active {
2468  background: linear-gradient(90deg, #f8fafc 0%, #f1f5f9 100%);
2469  padding-left: 20px;
2470}
2471
2472.suggest-thumb {
2473  width: 42px;
2474  height: 42px;
2475  object-fit: cover;
2476  border: 1px solid #ddd;
2477  border-radius: 4px;
2478  background: #fafafa;
2479}
2480
2481.suggest-meta { flex: 1; min-width: 0; }
2482
2483.suggest-name {
2484  font-weight: 500;
2485  color: #222;
2486  line-height: 1.2;
2487  white-space: nowrap;
2488  overflow: hidden;
2489  text-overflow: ellipsis;
2490}
2491
2492.suggest-price {
2493  color: #d32f2f;
2494  font-size: 12px;
2495  margin-top: 2px;
2496}
2497
2498/* AI Insights */
2499.suggest-ai-insight {
2500  display: flex;
2501  align-items: center;
2502  gap: 8px;
2503  padding: 8px 12px;
2504  background: linear-gradient(135deg, #FFFBEB 0%, #FEF9C3 100%);
2505  border-bottom: 1px solid #FDE68A;
2506  color: #92400E;
2507  font-size: 12px;
2508  font-weight: 500;
2509  line-height: 1.4;
2510}
2511
2512.suggest-ai-insight svg {
2513  flex-shrink: 0;
2514  opacity: 0.8;
2515}
2516
2517.suggest-ai-insight span {
2518  flex: 1;
2519}
2520
2521/* AI Progress States */
2522.ai-progress-container {
2523  padding: 20px 16px;
2524  background: linear-gradient(135deg, #fafbff 0%, #f5f7ff 100%);
2525}
2526
2527.ai-progress-header {
2528  display: flex;
2529  align-items: center;
2530  gap: 10px;
2531  margin-bottom: 16px;
2532}
2533
2534.ai-progress-icon {
2535  width: 36px;
2536  height: 36px;
2537  display: flex;
2538  align-items: center;
2539  justify-content: center;
2540  background: linear-gradient(135deg, #EAB308 0%, #8B6914 100%);
2541  border-radius: 50%;
2542  color: #fff;
2543  font-size: 18px;
2544  animation: aiIconSpin 3s linear infinite;
2545}
2546
2547@keyframes aiIconSpin {
2548  0% { transform: rotate(0deg); }
2549  100% { transform: rotate(360deg); }
2550}
2551
2552.ai-progress-text {
2553  flex: 1;
2554}
2555
2556.ai-progress-title {
2557  font-weight: 600;
2558  color: #1e293b;
2559  font-size: 14px;
2560  margin-bottom: 2px;
2561}
2562
2563.ai-progress-subtitle {
2564  font-size: 12px;
2565  color: #64748b;
2566}
2567
2568/* Progress Steps */
2569.ai-progress-steps {
2570  display: flex;
2571  flex-direction: column;
2572  gap: 10px;
2573  margin-top: 16px;
2574}
2575
2576.ai-progress-step {
2577  display: flex;
2578  align-items: center;
2579  gap: 10px;
2580  padding: 8px 12px;
2581  background: #fff;
2582  border-radius: 8px;
2583  border: 1px solid #e2e8f0;
2584  transition: all 0.3s ease;
2585}
2586
2587.ai-progress-step.active {
2588  border-color: #EAB308;
2589  background: #f8f9ff;
2590  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
2591}
2592
2593.ai-progress-step.completed {
2594  border-color: #10b981;
2595  background: #f0fdf4;
2596}
2597
2598.ai-step-icon {
2599  width: 20px;
2600  height: 20px;
2601  display: flex;
2602  align-items: center;
2603  justify-content: center;
2604  border-radius: 50%;
2605  font-size: 11px;
2606  flex-shrink: 0;
2607}
2608
2609.ai-progress-step.active .ai-step-icon {
2610  background: #EAB308;
2611  color: #fff;
2612  animation: pulse 1.5s ease-in-out infinite;
2613}
2614
2615.ai-progress-step.completed .ai-step-icon {
2616  background: #10b981;
2617  color: #fff;
2618}
2619
2620.ai-progress-step.pending .ai-step-icon {
2621  background: #e2e8f0;
2622  color: #94a3b8;
2623}
2624
2625.ai-step-text {
2626  flex: 1;
2627  font-size: 13px;
2628  color: #475569;
2629  font-weight: 500;
2630}
2631
2632.ai-progress-step.completed .ai-step-text {
2633  color: #059669;
2634}
2635
2636/* AI Analysis Result Banner */
2637.ai-result-banner {
2638  padding: 12px 16px;
2639  background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
2640  border-bottom: 1px solid #86efac;
2641  display: flex;
2642  align-items: center;
2643  gap: 10px;
2644}
2645
2646.ai-result-icon {
2647  font-size: 20px;
2648  animation: bounceIn 0.5s ease;
2649}
2650
2651@keyframes bounceIn {
2652  0% { transform: scale(0); opacity: 0; }
2653  50% { transform: scale(1.1); }
2654  100% { transform: scale(1); opacity: 1; }
2655}
2656
2657.ai-result-text {
2658  flex: 1;
2659}
2660
2661.ai-result-title {
2662  font-weight: 600;
2663  color: #065f46;
2664  font-size: 13px;
2665  margin-bottom: 2px;
2666}
2667
2668.ai-result-description {
2669  font-size: 12px;
2670  color: #047857;
2671  opacity: 0.9;
2672}
2673
2674/* Product Items Section Header */
2675.suggest-section-header {
2676  padding: 10px 16px;
2677  background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
2678  border-bottom: 1px solid #e2e8f0;
2679}
2680
2681.suggest-header-main {
2682  display: flex;
2683  align-items: center;
2684  gap: 6px;
2685  margin-bottom: 4px;
2686}
2687
2688.suggest-header-icon {
2689  font-size: 14px;
2690}
2691
2692.suggest-header-text {
2693  font-size: 12px;
2694  font-weight: 600;
2695  color: #64748b;
2696  text-transform: uppercase;
2697  letter-spacing: 0.5px;
2698}
2699
2700.suggest-header-hint {
2701  font-size: 11px;
2702  color: #94a3b8;
2703  font-weight: 400;
2704  text-transform: none;
2705  letter-spacing: 0;
2706  display: flex;
2707  align-items: center;
2708  gap: 4px;
2709}
2710
2711.suggest-header-hint kbd {
2712  display: inline-block;
2713  padding: 2px 6px;
2714  font-size: 10px;
2715  font-weight: 600;
2716  line-height: 1;
2717  color: #475569;
2718  background: #fff;
2719  border: 1px solid #cbd5e1;
2720  border-radius: 4px;
2721  box-shadow: 0 1px 2px rgba(0,0,0,0.05);
2722  font-family: 'Segoe UI', monospace;
2723}
2724
2725/* Footer Hint */
2726.suggest-footer-hint {
2727  display: flex;
2728  align-items: center;
2729  justify-content: center;
2730  padding: 12px 16px;
2731  background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);
2732  border-top: 1px solid #EAB308;
2733  color: #8B6914;
2734  font-size: 12px;
2735  font-weight: 500;
2736  text-align: center;
2737  cursor: default;
2738}
2739
2740.suggest-footer-hint svg {
2741  color: #CA8A04;
2742  flex-shrink: 0;
2743}
2744
2745.suggest-footer-hint kbd {
2746  display: inline-block;
2747  padding: 3px 7px;
2748  margin: 0 3px;
2749  font-size: 11px;
2750  font-weight: 600;
2751  line-height: 1;
2752  color: #78350F;
2753  background: #fff;
2754  border: 1px solid #CA8A04;
2755  border-radius: 4px;
2756  box-shadow: 0 1px 3px rgba(245, 158, 11, 0.2);
2757  font-family: 'Segoe UI', monospace;
2758}
2759
2760.suggest-footer-link {
2761  color: #CA8A04;
2762  font-weight: 600;
2763  text-decoration: underline;
2764  text-underline-offset: 2px;
2765  text-decoration-thickness: 1px;
2766  cursor: pointer;
2767  transition: all 0.2s ease;
2768}
2769
2770.suggest-footer-link:hover {
2771  color: #8B6914;
2772  text-decoration-thickness: 2px;
2773  text-shadow: 0 0 8px rgba(234, 88, 12, 0.3);
2774}
2775
2776/* Loading state */
2777.suggest-loading {
2778  display: flex;
2779  align-items: center;
2780  justify-content: center;
2781  gap: 8px;
2782  padding: 16px 12px;
2783  color: #64748b;
2784  font-size: 13px;
2785}
2786
2787@keyframes pulse {
2788  0%, 100% { opacity: 1; }
2789  50% { opacity: 0.5; }
2790}
2791
2792.suggest-loading {
2793  animation: pulse 1.5s ease-in-out infinite;
2794}
2795
2796/* Responsive Mobile Styles */
2797@media (max-width: 768px) {
2798  .ai-bot-toggle {
2799    padding: 6px;
2800    margin-left: 0;
2801  }
2802  
2803  .ai-bot-toggle .bot-icon {
2804    width: 20px;
2805    height: 20px;
2806  }
2807  
2808  .search-icon-btn {
2809    margin-right: 4px;
2810  }
2811  
2812  .search-icon-btn svg {
2813    width: 18px;
2814    height: 18px;
2815  }
2816  
2817  .search-suggest {
2818    max-height: 350px;
2819  }
2820  
2821  /* Mobile hint adjustments */
2822  .suggest-header-hint {
2823    font-size: 10px;
2824  }
2825  
2826  .suggest-header-hint kbd {
2827    padding: 1px 4px;
2828    font-size: 9px;
2829  }
2830  
2831  .suggest-footer-hint {
2832    padding: 10px 12px;
2833    font-size: 11px;
2834  }
2835  
2836  .suggest-footer-hint svg {
2837    width: 14px;
2838    height: 14px;
2839  }
2840  
2841  .suggest-footer-hint kbd {
2842    padding: 2px 5px;
2843    font-size: 10px;
2844  }
2845  
2846  .ai-progress-container {
2847    padding: 16px 12px;
2848  }
2849  
2850  .ai-progress-icon {
2851    width: 32px;
2852    height: 32px;
2853    font-size: 16px;
2854  }
2855  
2856  .ai-progress-title {
2857    font-size: 13px;
2858  }
2859  
2860  .ai-progress-subtitle {
2861    font-size: 11px;
2862  }
2863  
2864  .ai-progress-step {
2865    padding: 6px 10px;
2866  }
2867  
2868  .ai-step-text {
2869    font-size: 12px;
2870  }
2871  
2872  .suggest-item {
2873    padding: 8px 12px;
2874  }
2875  
2876  .suggest-thumb {
2877    width: 36px;
2878    height: 36px;
2879  }
2880  
2881  .suggest-name {
2882    font-size: 13px;
2883  }
2884  
2885  .suggest-price {
2886    font-size: 11px;
2887  }
2888}
2889
2890@media (max-width: 480px) {
2891  .ai-bot-toggle {
2892    padding: 4px;
2893  }
2894  
2895  .ai-bot-toggle .bot-icon {
2896    width: 18px;
2897    height: 18px;
2898  }
2899  
2900  .search-icon-btn svg {
2901    width: 16px;
2902    height: 16px;
2903  }
2904  
2905  /* Ultra-compact hints for small screens */
2906  .suggest-header-hint {
2907    display: none; /* Hide header hint on very small screens */
2908  }
2909  
2910  .suggest-footer-hint {
2911    padding: 8px 10px;
2912    font-size: 10px;
2913    flex-direction: column;
2914    gap: 4px;
2915  }
2916  
2917  .suggest-footer-hint svg {
2918    display: none; /* Hide icon to save space */
2919  }
2920  
2921  .suggest-footer-hint kbd {
2922    padding: 2px 4px;
2923    font-size: 9px;
2924  }
2925  
2926  .ai-progress-steps {
2927    gap: 8px;
2928  }
2929  
2930  .ai-step-text {
2931    font-size: 11px;
2932  }
2933}
2934
2935/* AI Search Prompt Box */
2936.ai-search-prompt {
2937  padding: 24px;
2938  background: linear-gradient(135deg, #FFFBEB 0%, #ffffff 100%);
2939}
2940
2941.ai-prompt-header {
2942  display: flex;
2943  align-items: center;
2944  gap: 12px;
2945  margin-bottom: 16px;
2946}
2947
2948.ai-prompt-icon {
2949  width: 48px;
2950  height: 48px;
2951  background: linear-gradient(135deg, #FBBF24 0%, #CA8A04 100%);
2952  border-radius: 12px;
2953  display: flex;
2954  align-items: center;
2955  justify-content: center;
2956  font-size: 24px;
2957  box-shadow: 0 4px 12px rgba(251, 191, 36, 0.3);
2958}
2959
2960.ai-prompt-text {
2961  flex: 1;
2962}
2963
2964.ai-prompt-title {
2965  font-size: 16px;
2966  font-weight: 700;
2967  color: #1f2937;
2968  margin-bottom: 4px;
2969}
2970
2971.ai-prompt-subtitle {
2972  font-size: 13px;
2973  color: #6b7280;
2974}
2975
2976.ai-prompt-query {
2977  background: white;
2978  border: 2px solid #FBBF24;
2979  border-radius: 8px;
2980  padding: 12px 16px;
2981  margin-bottom: 16px;
2982}
2983
2984.ai-query-label {
2985  font-size: 11px;
2986  font-weight: 600;
2987  color: #FBBF24;
2988  text-transform: uppercase;
2989  letter-spacing: 0.5px;
2990  margin-bottom: 4px;
2991}
2992
2993.ai-query-text {
2994  font-size: 15px;
2995  font-weight: 600;
2996  color: #1f2937;
2997}
2998
2999.ai-search-trigger-btn {
3000  width: 100%;
3001  padding: 14px 20px;
3002  background: linear-gradient(135deg, #FBBF24 0%, #CA8A04 100%);
3003  color: white;
3004  border: none;
3005  border-radius: 10px;
3006  font-size: 15px;
3007  font-weight: 700;
3008  cursor: pointer;
3009  display: flex;
3010  align-items: center;
3011  justify-content: center;
3012  gap: 8px;
3013  transition: all 0.3s ease;
3014  box-shadow: 0 4px 12px rgba(251, 191, 36, 0.4);
3015}
3016
3017.ai-search-trigger-btn:hover {
3018  transform: translateY(-2px);
3019  box-shadow: 0 6px 20px rgba(251, 191, 36, 0.5);
3020}
3021
3022.ai-search-trigger-btn:active {
3023  transform: translateY(0);
3024}
3025
3026.ai-btn-icon {
3027  font-size: 18px;
3028  animation: sparkle 2s ease-in-out infinite;
3029}
3030
3031@keyframes sparkle {
3032  0%, 100% { opacity: 1; transform: scale(1); }
3033  50% { opacity: 0.7; transform: scale(1.2); }
3034}
3035
3036.ai-btn-text {
3037  font-size: 15px;
3038}
3039
3040.ai-prompt-footer {
3041  margin-top: 12px;
3042  text-align: center;
3043  font-size: 12px;
3044  color: #9ca3af;
3045  font-style: italic;
3046}
3047
3048/* ==================== TRENDING SEARCH STYLES ==================== */
3049
3050.search-suggest.trending-mode {
3051  border-top: 3px solid #FBBF24;
3052  max-width: 900px;
3053  min-width: 600px;
3054}
3055
3056/* Two-column container */
3057.trending-container {
3058  display: flex;
3059  gap: 0;
3060  padding: 0 !important;
3061}
3062
3063.trending-column {
3064  flex: 1;
3065  min-width: 0;
3066  padding: 0 !important;
3067}
3068
3069.trending-column.trending-searches {
3070  border-right: 2px solid #e2e8f0; padding: 0 !important;
3071}
3072
3073/* Mobile: stack columns vertically */
3074@media (max-width: 768px) {
3075  .search-suggest.trending-mode {
3076    min-width: auto;
3077    max-width: 100%;
3078  }
3079  
3080  .trending-container {
3081    flex-direction: column;
3082  }
3083  
3084  .trending-column.trending-searches {
3085    border-right: none;
3086    border-bottom: 2px solid #e2e8f0;
3087  }
3088}
3089
3090.trending-header {
3091  display: flex;
3092  align-items: center;
3093  gap: 8px;
3094  padding: 14px 16px;
3095  border-bottom: 1px solid #e2e8f0;
3096  background: linear-gradient(135deg, #FBBF24 0%, #CA8A04 100%);
3097  color: white;
3098  position: sticky;
3099  top: 0;
3100  z-index: 10;
3101}
3102
3103.trending-icon {
3104  font-size: 20px;
3105  animation: trendingPulse 2s infinite;
3106}
3107
3108@keyframes trendingPulse {
3109  0%, 100% { 
3110    transform: scale(1); 
3111    filter: drop-shadow(0 0 0 rgba(251, 191, 36, 0));
3112  }
3113  50% { 
3114    transform: scale(1.15); 
3115    filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.8));
3116  }
3117}
3118
3119.trending-title {
3120  font-weight: 700;
3121  font-size: 14px;
3122  letter-spacing: 0.3px;
3123}
3124
3125.trending-subtitle {
3126  font-size: 11px;
3127  opacity: 0.9;
3128  margin-left: auto;
3129  font-style: italic;
3130}
3131
3132/* Trending searches item */
3133.trending-item {
3134  display: flex;
3135  align-items: center;
3136  gap: 12px;
3137  padding: 12px 16px;
3138  cursor: pointer;
3139  transition: all 0.2s ease;
3140  border-bottom: 1px solid #f1f5f9;
3141  position: relative;
3142}
3143
3144.trending-item:hover {
3145  background: linear-gradient(90deg, #FFFBEB 0%, #FEF3C7 100%);
3146  padding-left: 20px;
3147}
3148
3149.trending-item:hover::before {
3150  content: '';
3151  position: absolute;
3152  left: 0;
3153  top: 0;
3154  bottom: 0;
3155  width: 4px;
3156  background: linear-gradient(180deg, #FBBF24 0%, #CA8A04 100%);
3157}
3158
3159.trending-item:last-child {
3160  border-bottom: none;
3161}
3162
3163.trending-rank {
3164  font-size: 20px;
3165  flex-shrink: 0;
3166  width: 32px;
3167  text-align: center;
3168}
3169
3170.trending-query {
3171  flex: 1;
3172  font-size: 14px;
3173  color: #1f2937;
3174  font-weight: 500;
3175  line-height: 1.4;
3176}
3177
3178.trending-category {
3179  font-size: 11px;
3180  color: #CA8A04;
3181  background: #FFFBEB;
3182  padding: 3px 10px;
3183  border-radius: 12px;
3184  white-space: nowrap;
3185  font-weight: 500;
3186  border: 1px solid #FDE68A;
3187}
3188
3189/* Trending products item */
3190.trending-product-item {
3191  display: flex;
3192  align-items: center;
3193  gap: 12px;
3194  padding: 12px 16px;
3195  cursor: pointer;
3196  transition: all 0.2s ease;
3197  border-bottom: 1px solid #f1f5f9;
3198  position: relative;
3199}
3200
3201.trending-product-item:hover {
3202  background: linear-gradient(90deg, #FEF3C7 0%, #FDE68A 100%);
3203  padding-left: 20px;
3204}
3205
3206.trending-product-item:hover::before {
3207  content: '';
3208  position: absolute;
3209  left: 0;
3210  top: 0;
3211  bottom: 0;
3212  width: 4px;
3213  background: linear-gradient(180deg, #EAB308 0%, #CA8A04 100%);
3214}
3215
3216.trending-product-item:last-child {
3217  border-bottom: none;
3218}
3219
3220.trending-product-thumb {
3221  width: 48px;
3222  height: 48px;
3223  object-fit: cover;
3224  border-radius: 8px;
3225  border: 1px solid #e5e7eb;
3226  flex-shrink: 0;
3227  background: white;
3228}
3229
3230.trending-product-info {
3231  flex: 1;
3232  min-width: 0;
3233}
3234
3235.trending-product-name {
3236  font-size: 13px;
3237  font-weight: 400;
3238  color: #1f2937;
3239  line-height: 1.4;
3240  margin-bottom: 0;
3241  padding: 0 !important;
3242  overflow: hidden;
3243  text-overflow: ellipsis;
3244  display: -webkit-box;
3245  -webkit-line-clamp: 2;
3246  -webkit-box-orient: vertical;
3247}
3248
3249.trending-product-price {
3250  font-size: 12px;
3251  font-weight: 700;
3252  color: #CA8A04;
3253  padding: 4px 0 !important;
3254}
3255.trending-product { padding: 0;}
3256/* ==================== END TRENDING STYLES ==================== */
3257
3258</style>
3259  <style data-inlined="product-detail.css">
3260/* === PRODUCT DETAIL v3 (Tokopedia-like) — Full stylesheet === */
3261
3262/* Root / Container */
3263.product-single {
3264  --pd-gap: 2rem;
3265  --pd-border: #e2e8f0;
3266  --pd-bg: #ffffff;
3267  --pd-muted: #64748b;
3268  --pd-dark: #0f172a;
3269  --pd-accent: #0d6efd; /* tema utama situs */
3270  --pd-radius: 12px;
3271  --pd-soft: #f8fafc;
3272  --pd-soft-border: #e5e7eb;
3273  --pd-danger: #dc2626;
3274  --pd-success-bg: #dcfce7;
3275  --pd-success-text: #166534;
3276  --pd-danger-bg: #fee2e2;
3277  --pd-danger-text: #b91c1c;
3278  --pd-shadow: 0 6px 20px -10px rgba(0,0,0,.12);
3279  --pd-transition: .25s cubic-bezier(.4,.0,.2,1);
3280  max-width:1280px;
3281  margin-inline:auto;
3282}
3283
3284/* === Sticky Product Header Bar (muncul saat scroll) === */
3285.pd-sticky-header {
3286  position: fixed;
3287  top: 0;
3288  left: 0;
3289  right: 0;
3290  z-index: 998;
3291  background: rgba(255, 255, 255, 0.98);
3292  backdrop-filter: blur(12px);
3293  -webkit-backdrop-filter: blur(12px);
3294  border-bottom: 1px solid rgba(226, 232, 240, 0.8);
3295  box-shadow: 0 4px 16px -4px rgba(0, 0, 0, 0.08);
3296  transform: translateY(-100%);
3297  opacity: 0;
3298  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), 
3299              opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
3300  pointer-events: none;
3301}
3302
3303.pd-sticky-header.visible {
3304  transform: translateY(0);
3305  opacity: 1;
3306  pointer-events: auto;
3307}
3308
3309.pd-sticky-header-container {
3310  max-width: 1280px;
3311  margin: 0 auto;
3312  padding: 12px 20px;
3313  display: flex;
3314  align-items: center;
3315  gap: 16px;
3316}
3317
3318.pd-sticky-thumb {
3319  width: 48px;
3320  height: 48px;
3321  flex-shrink: 0;
3322  border-radius: 8px;
3323  border: 1px solid #e2e8f0;
3324  overflow: hidden;
3325  background: #f8fafc;
3326}
3327
3328.pd-sticky-thumb img {
3329  width: 100%;
3330  height: 100%;
3331  object-fit: cover;
3332  display: block;
3333}
3334
3335.pd-sticky-info {
3336  flex: 1;
3337  min-width: 0;
3338  display: flex;
3339  flex-direction: column;
3340  gap: 4px;
3341}
3342
3343.pd-sticky-title {
3344  font-size: 0.9rem;
3345  font-weight: 600;
3346  color: #0f172a;
3347  line-height: 1.3;
3348  white-space: nowrap;
3349  overflow: hidden;
3350  text-overflow: ellipsis;
3351  margin: 0;
3352}
3353
3354.pd-sticky-price-row {
3355  display: flex;
3356  align-items: center;
3357  gap: 8px;
3358  flex-wrap: wrap;
3359}
3360
3361.pd-sticky-price {
3362  font-size: 1.1rem;
3363  font-weight: 700;
3364  color: #0d6efd;
3365  line-height: 1;
3366}
3367
3368.pd-sticky-price-original {
3369  font-size: 0.75rem;
3370  color: #94a3b8;
3371  text-decoration: line-through;
3372  line-height: 1;
3373}
3374
3375.pd-sticky-discount {
3376  background: #dc2626;
3377  color: #fff;
3378  font-size: 0.65rem;
3379  font-weight: 600;
3380  padding: 2px 6px;
3381  border-radius: 4px;
3382  letter-spacing: 0.3px;
3383  line-height: 1;
3384}
3385
3386.pd-sticky-actions {
3387  display: flex;
3388  gap: 8px;
3389  flex-shrink: 0;
3390  align-items: center;
3391}
3392
3393.pd-sticky-share-btn {
3394  padding: 8px 16px;
3395  border-radius: 8px;
3396  border: 1px solid #e2e8f0;
3397  background: #fff;
3398  color: #64748b;
3399  cursor: pointer;
3400  transition: all 0.2s ease;
3401  display: inline-flex;
3402  align-items: center;
3403  justify-content: center;
3404  flex-shrink: 0;
3405}
3406
3407.pd-sticky-share-btn:hover {
3408  background: #f8fafc;
3409  border-color: #cbd5e1;
3410  color: #0f172a;
3411  transform: translateY(-1px);
3412}
3413
3414.pd-sticky-share-btn:active {
3415  transform: translateY(0);
3416}
3417
3418.pd-sticky-share-btn svg {
3419  display: block;
3420  width: 16px;
3421  height: 16px;
3422}
3423
3424.pd-sticky-btn-chat svg {
3425  width: 16px !important;
3426  height: 16px !important;
3427  max-width: none !important;
3428  margin-right: 4px;
3429  flex-shrink: 0;
3430}
3431
3432.pd-sticky-btn {
3433  padding: 8px 16px;
3434  border-radius: 8px;
3435  font-size: 0.85rem;
3436  font-weight: 600;
3437  cursor: pointer;
3438  transition: all 0.2s ease;
3439  border: 0;
3440  display: inline-flex;
3441  align-items: center;
3442  justify-content: center;
3443  white-space: nowrap;
3444}
3445
3446.pd-sticky-btn-cart {
3447  background: #fff;
3448  color: #0d6efd;
3449  border: 1px solid #0d6efd;
3450}
3451
3452.pd-sticky-btn-cart:hover {
3453  background: rgba(13, 110, 253, 0.06);
3454}
3455
3456.pd-sticky-btn-chat {
3457  background: #fff;
3458  color: #64748b;
3459  border: 1px solid #cbd5e1;
3460  flex: 0.8;
3461}
3462
3463.pd-sticky-btn-chat:hover {
3464  background: #f8fafc;
3465  border-color: #94a3b8;
3466  color: #475569;
3467}
3468
3469.pd-sticky-btn-buy {
3470  background: #0d6efd;
3471  color: #fff;
3472  border: 1px solid #0d6efd;
3473  box-shadow: 0 2px 8px -2px rgba(13, 110, 253, 0.4);
3474}
3475
3476.pd-sticky-btn-buy:hover {
3477  filter: brightness(0.95);
3478}
3479
3480.pd-sticky-btn:disabled {
3481  opacity: 0.5;
3482  cursor: not-allowed;
3483}
3484
3485/* Hide on mobile (use existing FAB instead) */
3486@media (max-width: 880px) {
3487  .pd-sticky-header {
3488    display: none;
3489  }
3490}
3491
3492/* === PRINT STYLES (for PDF Export) === */
3493@media print {
3494  /* Reset & Base */
3495  * {
3496    -webkit-print-color-adjust: exact !important;
3497    print-color-adjust: exact !important;
3498  }
3499  
3500  body {
3501    background: white !important;
3502    margin: 0;
3503    padding: 0;
3504  }
3505  
3506  /* Hide all navigation, buttons, and interactive elements */
3507  nav, header, footer,
3508  nav *, header *, footer *,
3509  .navbar, .navbar *,
3510  .nav-item, .nav-link,
3511  .pd-sticky-header,
3512  .pd-sticky-header.visible,
3513  .pd-breadcrumb,
3514  .pd-sidebar,
3515  .pd-mobile-fab,
3516  .pd-share-btn,
3517  .pd-sticky-share-btn,
3518  #pd-share-btn,
3519  .share-button,
3520  [data-share-btn],
3521  button,
3522  .btn-add-cart,
3523  .btn-buy,
3524  .pd-actions,
3525  .pd-compare-section,
3526  .pd-compare-btn,
3527  .pd-compare-info,
3528  #pd-compare-btn,
3529  .compare-note,
3530  .compare-container,
3531  .compare-dropdown,
3532  .compare-panel,
3533  .compare-wrapper,
3534  [data-compare],
3535  [id*="compare"],
3536  [class*="compare"],
3537  .pd-thumb-gallery,
3538  .pd-installment-card,
3539  .pd-installment-card2,
3540  .inst-alt-banner--compact,
3541  .qoala-banner-card,
3542  .tradein-compact-card,
3543  .pwp-deals-section,
3544  #pwp-deals-section,
3545  .pwp-trigger-badge,
3546  .pd-related-full,
3547  #pd-similar,
3548  .pd-similar-strip,
3549  .pd-scroll-top,
3550  .stock-info-btn,
3551  .stock-popover,
3552  .pd-countdown-modern,
3553  .pd-share-modal-overlay,
3554  [id*="promo-"],
3555  .bottom-nav,
3556  .pd-buy-form,
3557  .pd-buy-card,
3558  .pd-extra-meta,
3559  .pd-reviews-section,
3560  #reviews,
3561  hr,
3562  .divider,
3563  .separator {
3564    display: none !important;
3565    visibility: hidden !important;
3566    opacity: 0 !important;
3567    height: 0 !important;
3568    max-height: 0 !important;
3569    overflow: hidden !important;
3570    margin: 0 !important;
3571    padding: 0 !important;
3572    border: none !important;
3573  }
3574  
3575  /* Extra aggressive reset for any remaining borders */
3576  .pd-info-card *,
3577  .pd-meta-inline *,
3578  .pd-variants * {
3579    border: none !important;
3580    border-top: none !important;
3581    border-bottom: none !important;
3582  }
3583  
3584  /* Layout restructure */
3585  .product-single {
3586    max-width: 100% !important;
3587    margin: 0 !important;
3588    padding: 0 !important;
3589    box-shadow: none !important;
3590  }
3591  
3592  /* Reset all shadows and borders for print */
3593  * {
3594    box-shadow: none !important;
3595  }
3596  
3597  .pd-top-layout {
3598    display: grid !important;
3599    grid-template-columns: 280px 1fr !important;
3600    gap: 24px !important;
3601    margin: 0 0 24px 0 !important;
3602    padding: 0 !important;
3603    page-break-inside: avoid;
3604    align-items: start !important;
3605  }
3606  
3607  /* Print header with logo - centered and larger */
3608  .product-single::before {
3609    content: "" !important;
3610    display: block !important;
3611    width: 150px !important;
3612    height: 50px !important;
3613    background: url('/assets//img/logo.png') no-repeat center !important;
3614    background-size: contain !important;
3615    margin: 0 auto 20px !important;
3616    visibility: visible !important;
3617    opacity: 1 !important;
3618  }
3619  
3620  /* Media card - left column */
3621  .pd-media-card {
3622    border: none !important;
3623    padding: 0 !important;
3624    margin: 0 !important;
3625    text-align: center;
3626    page-break-inside: avoid;
3627  }
3628  
3629  /* Hide desktop image, use mobile slider for both desktop and mobile */
3630  .pd-main-image {
3631    display: none !important;
3632  }
3633  
3634  /* Mobile slider - show first image only in print */
3635  .pd-mobile-slider {
3636    display: block !important;
3637    overflow: visible !important;
3638  }
3639  
3640  .pd-mslide {
3641    display: none !important;
3642  }
3643  
3644  .pd-mslide:first-child {
3645    display: block !important;
3646    border: 1px solid #e2e8f0 !important;
3647    max-width: 280px !important;
3648    margin: 0 auto !important;
3649    border-radius: 8px !important;
3650  }
3651  
3652  .pd-mslide:first-child img {
3653    max-height: 280px !important;
3654    width: 100% !important;
3655    height: auto !important;
3656    object-fit: contain !important;
3657  }
3658  
3659  /* Info card - right column */
3660  .pd-info-card {
3661    border: none !important;
3662    border-top: none !important;
3663    border-bottom: none !important;
3664    padding: 0 !important;
3665    margin: 0 !important;
3666    background: white !important;
3667    box-shadow: none !important;
3668  }
3669  
3670  /* Title */
3671  .pd-title-row {
3672    display: block !important;
3673    margin-bottom: 10px !important;
3674    border: none !important;
3675    border-top: none !important;
3676    border-bottom: none !important;
3677    padding: 0 !important;
3678    box-shadow: none !important;
3679    text-decoration: none !important;
3680  }
3681  
3682  /* Hide share button in print */
3683  .pd-title-row .pd-share-btn,
3684  .pd-title-row #pd-share-btn {
3685    display: none !important;
3686  }
3687  
3688  .pd-title {
3689    font-size: 13pt !important;
3690    color: #000 !important;
3691    margin: 0 0 10px 0 !important;
3692    page-break-after: avoid;
3693    font-weight: 700 !important;
3694    line-height: 1.3 !important;
3695    border: none !important;
3696    border-top: none !important;
3697    border-bottom: none !important;
3698    padding-bottom: 0 !important;
3699    box-shadow: none !important;
3700    text-decoration: none !important;
3701  }
3702  
3703  /* Price and QR container - side by side */
3704  .pd-price-qr-container {
3705    display: flex !important;
3706    gap: 16px !important;
3707    align-items: flex-start !important;
3708    margin-bottom: 14px !important;
3709    box-shadow: none !important;
3710    border: none !important;
3711    text-decoration: none !important;
3712  }
3713  
3714  /* Price block - left side */
3715  .pd-price-block {
3716    flex: 1 !important;
3717    margin: 0 !important;
3718    padding-bottom: 0 !important;
3719    border-bottom: none !important;
3720    box-shadow: none !important;
3721  }
3722  
3723  .pd-price-row {
3724    margin-bottom: 8px !important;
3725    border: none !important;
3726    border-top: none !important;
3727    border-bottom: none !important;
3728    padding: 0 !important;
3729  }
3730  
3731  /* Hide all empty or unnecessary elements in price row */
3732  .pd-price-row > span:empty,
3733  .pd-price-row > .sold-inline,
3734  #pd-sold-inline {
3735    display: none !important;
3736  }
3737  
3738  .pd-price-current {
3739    font-size: 18pt !important;
3740    color: #0d6efd !important;
3741  }
3742  
3743  /* Show price range for variant products in print */
3744  .pd-price-current[data-price-range]::after {
3745    content: attr(data-price-range) !important;
3746    display: block !important;
3747    font-size: 11pt !important;
3748    color: #64748b !important;
3749    margin-top: 4px !important;
3750    font-weight: normal !important;
3751  }
3752  
3753  /* Hide 'Range:' prefix text */
3754  .pd-price-current[data-price-range]::after {
3755    content: attr(data-price-range) !important;
3756  }
3757  
3758  .pd-price-original {
3759    font-size: 12pt !important;
3760    color: #64748b !important;
3761  }
3762  
3763  .pd-discount-badge {
3764    font-size: 10pt !important;
3765    padding: 2px 6px !important;
3766  }
3767  
3768  /* QR Code section - right side beside price */
3769  .pd-qr-print {
3770    display: block !important;
3771    flex: 0 0 100px !important;
3772    margin: 0 !important;
3773    padding: 8px !important;
3774    background: #f8fafc !important;
3775    border-radius: 8px !important;
3776    text-align: center !important;
3777    page-break-inside: avoid !important;
3778    border: 1px solid #cbd5e1 !important;
3779  }
3780  
3781  .pd-qr-label {
3782    font-size: 6.5pt !important;
3783    color: #64748b !important;
3784    margin-bottom: 5px !important;
3785    line-height: 1.2 !important;
3786    font-weight: 600 !important;
3787  }
3788  
3789  .pd-qr-image {
3790    width: 80px !important;
3791    height: 80px !important;
3792    display: block !important;
3793    margin: 0 auto !important;
3794  }
3795  
3796  /* Meta info */
3797  .pd-meta-inline {
3798    display: flex !important;
3799    gap: 12px !important;
3800    margin-bottom: 0 !important;
3801    padding-bottom: 14px !important;
3802    font-size: 10pt !important;
3803    border: none !important;
3804    border-top: none !important;
3805    border-bottom: none !important;
3806    padding-top: 0 !important;
3807    box-shadow: none !important;
3808    text-decoration: none !important;
3809    background: transparent !important;
3810  }
3811  
3812  /* Hide stock badge in print */
3813  .stock-badge,
3814  .stock-info-btn {
3815    display: none !important;
3816  }
3817  
3818  .sku {
3819    color: #64748b !important;
3820  }
3821  
3822  /* Hide sold badge if empty/zero */
3823  .sold-inline {
3824    display: none !important;
3825  }
3826  
3827  /* Hide variant summary section in print */
3828  .pd-variant-combined,
3829  #pd-variant-combined,
3830  .pd-variant-summary,
3831  [data-role="sold-chip"] {
3832    display: none !important;
3833  }
3834  
3835  /* Hide compare section completely in print */
3836  .pd-compare-section,
3837  .pd-compare-btn,
3838  #pd-compare-btn,
3839  .pd-compare-info,
3840  .compare-note,
3841  .compare-text,
3842  .compare-remove {
3843    display: none !important;
3844    visibility: hidden !important;
3845    height: 0 !important;
3846    margin: 0 !important;
3847    padding: 0 !important;
3848    overflow: hidden !important;
3849  }
3850  
3851  /* Variants - show all options with selected highlighted */
3852  .pd-variants {
3853    margin: 0 !important;
3854    padding: 0 !important;
3855    page-break-inside: avoid !important;
3856    border: none !important;
3857    border-top: none !important;
3858    border-bottom: none !important;
3859    box-shadow: none !important;
3860    background: transparent !important;
3861  }
3862  
3863  /* Add 'Variant:' label before variant groups */
3864  .pd-variants::before {
3865    content: "Variant:" !important;
3866    display: block !important;
3867    font-size: 11pt !important;
3868    font-weight: 700 !important;
3869    color: #0f172a !important;
3870    margin-bottom: 10px !important;
3871    border: none !important;
3872    padding: 0 !important;
3873    border-top: none !important;
3874    border-bottom: none !important;
3875  }
3876  
3877  .v-group {
3878    display: flex !important;
3879    align-items: center !important;
3880    gap: 8px !important;
3881    margin-bottom: 8px !important;
3882    line-height: 1 !important;
3883    page-break-inside: avoid !important;
3884    border: none !important;
3885    padding: 0 !important;
3886  }
3887  
3888  .v-group:last-child {
3889    margin-bottom: 0 !important;
3890  }
3891  
3892  .v-label {
3893    font-weight: 700 !important;
3894    margin: 0 !important;
3895    font-size: 9pt !important;
3896    color: #475569 !important;
3897    min-width: 60px !important;
3898    flex-shrink: 0 !important;
3899    border: none !important;
3900    padding: 0 !important;
3901  }
3902  
3903  .v-options {
3904    display: flex !important;
3905    gap: 6px !important;
3906    flex-wrap: wrap !important;
3907    border: none !important;
3908    padding: 0 !important;
3909    margin: 0 !important;
3910  }
3911  
3912  .v-chip {
3913    border: 1px solid #cbd5e1 !important;
3914    padding: 3px 10px !important;
3915    font-size: 9pt !important;
3916    background: #f8fafc !important;
3917    border-radius: 6px !important;
3918    display: inline-flex !important;
3919    align-items: center !important;
3920    page-break-inside: avoid !important;
3921  }
3922  
3923  /* Highlight selected variant with gold background */
3924  .v-chip.selected {
3925    background: #FFFBEB !important;
3926    border-color: #CA8A04 !important;
3927    font-weight: 600 !important;
3928  }
3929  
3930  /* Hide radio inputs */
3931  .v-chip input[type="radio"] {
3932    display: none !important;
3933  }
3934  
3935  /* Hide "Habis" badge in print */
3936  .v-chip .chip-badge {
3937    display: none !important;
3938  }
3939  
3940  /* Short description - HIDDEN in print */
3941  .pd-short-desc {
3942    display: none !important;
3943  }
3944  
3945  /* QR Code section - right side beside price */
3946  .pd-qr-print {
3947    display: block !important;
3948    flex: 0 0 100px !important;
3949    margin: 0 !important;
3950    padding: 8px !important;
3951    background: #f8fafc !important;
3952    border-radius: 8px !important;
3953    text-align: center !important;
3954    page-break-inside: avoid !important;
3955    border: 1px solid #cbd5e1 !important;
3956  }
3957  
3958  .pd-qr-label {
3959    font-size: 6.5pt !important;
3960    color: #64748b !important;
3961    margin-bottom: 5px !important;
3962    line-height: 1.2 !important;
3963    font-weight: 600 !important;
3964  }
3965  
3966  .pd-qr-image {
3967    width: 80px !important;
3968    height: 80px !important;
3969    display: block !important;
3970    margin: 0 auto !important;
3971  }
3972  
3973  /* Full description - below 2 column layout */
3974  .pd-full-description {
3975    border: none !important;
3976    padding: 0 !important;
3977    margin: 24px 0 0 0 !important;
3978    page-break-before: avoid;
3979    grid-column: 1 / -1 !important;
3980  }
3981  
3982  .pd-full-desc-title {
3983    font-size: 13pt !important;
3984    font-weight: 700 !important;
3985    margin: 0 0 12px 0 !important;
3986    color: #000 !important;
3987    border-top: none !important;
3988    border-bottom: none !important;
3989    padding-top: 16px !important;
3990  }
3991  
3992  .pd-full-desc-body {
3993    font-size: 10pt !important;
3994    line-height: 1.6 !important;
3995    color: #334155 !important;
3996  }
3997  
3998  .pd-full-desc-body img {
3999    max-height: 200px !important;
4000    width: auto !important;
4001    page-break-inside: avoid;
4002    display: none !important; /* Hide images in print to save space */
4003  }
4004  
4005  /* Also target #deskripsi img if exists */
4006  #deskripsi img {
4007    max-width: 350px !important;
4008    height: auto !important;
4009  }
4010  
4011  .pd-full-desc-body table {
4012    border-collapse: collapse !important;
4013    width: 100% !important;
4014    margin: 12px 0 !important;
4015    font-size: 9pt !important;
4016  }
4017  
4018  .pd-full-desc-body th,
4019  .pd-full-desc-body td {
4020    border: 1px solid #cbd5e1 !important;
4021    padding: 6px 8px !important;
4022  }
4023  
4024  /* Print footer */
4025  .product-single::after {
4026    content: "Plaza IT - plazait.co.id | Printed: " attr(data-print-date);
4027    display: block;
4028    margin-top: 30px;
4029    padding-top: 16px;
4030    border-top: 1px solid #e2e8f0;
4031    font-size: 9pt;
4032    color: #64748b;
4033    text-align: center;
4034  }
4035  
4036  /* Page breaks */
4037  .pd-media-card,
4038  .pd-price-block,
4039  .pd-full-desc-title {
4040    page-break-after: avoid;
4041  }
4042  
4043  .pd-full-description {
4044    page-break-before: auto;
4045  }
4046}
4047
4048/* === Breadcrumb (truncate last item) === */
4049.pd-breadcrumb{
4050  display:flex;
4051  flex-wrap:nowrap;
4052  align-items:center;
4053  gap:.5rem;
4054  font-size:.72rem;
4055  margin:.7rem 0 1.2rem .5rem;
4056  color:var(--pd-muted);
4057  overflow:hidden;
4058}
4059.pd-breadcrumb a{color:var(--pd-accent);text-decoration:none}
4060.pd-breadcrumb a:hover{text-decoration:underline}
4061.pd-breadcrumb a,
4062.pd-breadcrumb span{flex:0 0 auto}
4063.pd-breadcrumb .current{
4064  flex:1 1 auto;
4065  min-width:0;
4066  white-space:nowrap;
4067  overflow:hidden;
4068  text-overflow:ellipsis;
4069  font-weight:600;
4070  color:var(--pd-dark);
4071  padding-right:2px;
4072}
4073
4074/* === Top layout === */
4075.pd-top-layout{
4076  display:grid;
4077  gap:var(--pd-gap);
4078  grid-template-columns:minmax(260px,340px) minmax(460px,1fr) 300px;
4079  align-items:start;
4080  margin-bottom:2.4rem;
4081}
4082
4083/* === Media column === */
4084.pd-media-card{
4085  background:#fff;
4086  border:1px solid var(--pd-border);
4087  border-radius:var(--pd-radius);
4088  padding:1rem 1rem 1.2rem;
4089}
4090.pd-main-image{margin:0 0 .8rem;border:1px solid var(--pd-soft-border);border-radius:10px;overflow:hidden;background:#fff;position:relative}
4091.pd-main-image img{width:100%;display:block;object-fit:contain;aspect-ratio:1/1}
4092
4093/* Gallery Thumbnails */
4094.pd-thumb-gallery{margin-top:.6rem;display:flex;flex-wrap:wrap;gap:.55rem}
4095.pd-thumb-btn{
4096  border:1px solid var(--pd-soft-border);
4097  background:#fff;
4098  cursor:pointer;
4099  width:64px;height:64px;padding:2px;
4100  display:flex;align-items:center;justify-content:center;
4101  overflow:hidden;border-radius:8px;
4102  transition:border-color .18s ease, box-shadow .18s ease, transform .18s ease;
4103  position:relative;
4104}
4105.pd-thumb-btn img{max-width:100%;max-height:100%;object-fit:contain;display:block}
4106.pd-thumb-btn:hover{border-color:var(--pd-accent)}
4107.pd-thumb-btn.active{border-color:var(--pd-accent);box-shadow:0 0 0 2px rgba(13,110,253,.25)}
4108.pd-thumb-btn:focus-visible{outline:2px solid var(--pd-accent);outline-offset:2px}
4109.pd-thumb-btn:active{transform:scale(.96)}
4110
4111/* === Info column === */
4112.pd-info-card{
4113  background:#fff;border:1px solid var(--pd-border);border-radius:var(--pd-radius);
4114  padding:1.4rem 1.6rem 2rem;
4115}
4116.pd-title{margin:0 0 1rem;font-size:1.2rem;line-height:1.25;font-weight:600;color:var(--pd-dark)}
4117.pd-price-block{margin:0 0 1rem}
4118.pd-price-row{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem;margin:0 0 .35rem}
4119.pd-price-current{font-size:2rem;font-weight:700;color:var(--pd-accent)}
4120.pd-price-original{font-size:.9rem;text-decoration:line-through;color:#94a3b8}
4121.pd-discount-badge{background:var(--pd-danger);color:#fff;font-size:.68rem;padding:4px 8px 3px;font-weight:600;border-radius:6px;letter-spacing:.5px}
4122.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}
4123.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}
4124.stock-badge.in{background:var(--pd-success-bg);color:var(--pd-success-text)}
4125.stock-badge.out{background:var(--pd-danger-bg);color:var(--pd-danger-text)}
4126.digital-badge{padding:4px 10px;border-radius:24px;background:linear-gradient(135deg,#C9A227 0%,#8B6914 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}
4127.sku{color:var(--pd-muted)}
4128.pd-short-desc{font-size:.85rem;line-height:1.5;margin:0 0 1.2rem;color:#334155;overflow-wrap:anywhere}
4129.pd-short-desc p{margin:0 0 .75rem}
4130.pd-short-desc ul,.pd-short-desc ol{margin:.5rem 0 .9rem 1.1rem;font-size:.85rem;line-height:1.45}
4131
4132/* Meta row for sold and rating badges */
4133.pd-meta-row {
4134  display: flex;
4135  align-items: center;
4136  flex-wrap: wrap;
4137  gap: 5px;
4138  margin-top: 8px;
4139  margin-bottom: 4px;
4140}
4141
4142/* Sales (Terjual) badge inline */
4143.sold-inline{
4144  display:inline-block;font-size:.65rem;font-weight:600;color:#475569;
4145  background:#f1f5f9;padding:3px 8px 4px;border-radius:6px;letter-spacing:.4px;margin-left:0;
4146}
4147
4148/* Price row divider */
4149.price-divider {
4150  display: inline-block;
4151  margin: 0 .4rem;
4152  color: #cbd5e1;
4153  font-weight: 400;
4154}
4155
4156/* Rating badge inline */
4157.rating-inline {
4158  display: inline-flex;
4159  align-items: center;
4160  gap: 4px;
4161  padding: 3px 8px 4px;
4162  background: #FFFBEB;
4163  border-radius: 6px;
4164  font-size: .65rem;
4165  font-weight: 600;
4166  color: #CA8A04;
4167  text-decoration: none;
4168  transition: all .2s ease;
4169  cursor: pointer;
4170  margin-left: .5rem;
4171  white-space: nowrap;
4172}
4173
4174.rating-inline:hover {
4175  background: #FEF3C7;
4176  transform: translateY(-1px);
4177  box-shadow: 0 2px 4px rgba(234, 88, 12, 0.1);
4178}
4179
4180.rating-inline .star-icon {
4181  font-size: .75rem;
4182  color: #CA8A04;
4183  line-height: 1;
4184}
4185
4186.rating-inline .rating-value {
4187  color: #CA8A04;
4188}
4189
4190.rating-inline .rating-badge-count {
4191  color: #78350F;
4192  opacity: 0.85;
4193}
4194
4195@media (max-width: 640px) {
4196  .rating-inline {
4197    font-size: .65rem;
4198    padding: 3px 8px 4px;
4199  }
4200  .rating-inline .star-icon {
4201    font-size: .7rem;
4202  }
4203}
4204
4205/* Highlight animation for review section */
4206@keyframes highlightFlash {
4207  0%, 100% { background-color: transparent; }
4208  50% { background-color: rgba(251, 191, 36, 0.1); }
4209}
4210
4211.highlight-flash {
4212  animation: highlightFlash 1.5s ease;
4213}
4214
4215/* === Variant chips === */
4216.pd-variants{margin-top:14px}
4217.v-group{margin-bottom:12px}
4218.v-label{font-weight:600;margin-bottom:6px}
4219.v-options{display:flex;flex-wrap:wrap;gap:8px}
4220.v-chip{
4221  display:inline-flex;align-items:center;gap:8px;
4222  padding:6px 10px;border:1px solid #d6d9de;border-radius:999px;
4223  cursor:pointer;background:#fff;transition:.15s ease;user-select:none
4224}
4225.v-chip:hover{border-color:#9aa3af;box-shadow:0 1px 0 rgba(0,0,0,.04)}
4226.v-chip.selected{border-color:#CA8A04;background:#FFFBEB}
4227.v-chip.disabled{opacity:.45;cursor:not-allowed}
4228.v-chip.out{border-style:dashed}
4229.v-chip input{display:none}
4230.v-chip .dot{width:14px;
4230height:14px;border-radius:50%;background:var(--dot,#999);box-shadow:inset 0 0 0 1px rgba(0,0,0,.2)}
4231.v-chip .dot--neutral{background:#9aa3af}
4232.v-chip .txt{font-size:.95rem;white-space:nowrap}
4233.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}
4234.pd-variant-summary{margin-top:8px;color:#475569;font-size:.92rem}
4235.pd-variant-summary .vs-item{display:inline-flex;gap:4px;margin-right:10px}
4236
4237/* === Buy form (di sidebar kanan untuk desktop) === */
4238.pd-buy-form{display:flex;flex-wrap:wrap;align-items:flex-end;gap:1rem 1.4rem;margin:0}
4239.qty-label{display:block;font-size:.6rem;font-weight:600;letter-spacing:.7px;text-transform:uppercase;color:var(--pd-muted);margin-bottom:.35rem}
4240.pd-buy-form input[type=number]{width:90px;padding:.55rem .6rem;border:1px solid var(--pd-soft-border);border-radius:6px;font-size:.85rem}
4241.pd-actions{display:flex;gap:.8rem;flex-wrap:wrap}
4242
4243/* Tombol senada tema */
4244.btn-add-cart,.btn-buy,.btn-chat-icon{
4245  cursor:pointer;border:0;font-weight:700;border-radius:10px;font-size:.9rem;
4246  display:inline-flex;align-items:center;justify-content:center;gap:.4rem;
4247  min-height:44px;padding:0 1rem;transition:var(--pd-transition)
4248}
4249.btn-add-cart{background:var(--brand-500);color:#fff;box-shadow:0 2px 8px -2px rgba(13,110,253,.35)}
4250.btn-add-cart:hover{filter:brightness(.95)}
4251.btn-buy{background:#fff;color:var(--brand-500);border:2px solid var(--brand-500)}
4252.btn-buy:hover{background:rgba(13,110,253,.06)}
4253.btn-chat-icon{background:#fff;color:#FBBF24;border:2px solid #FBBF24;padding:0;width:44px;min-width:44px}
4254.btn-chat-icon:hover{background:rgba(251, 191, 36, 0.06);border-color:#CA8A04}
4255.btn-chat-icon svg{width:20px;height:20px;flex-shrink:0}
4256.btn-add-cart:disabled,.btn-buy:disabled,.btn-chat-icon:disabled{opacity:.5;cursor:not-allowed}
4257
4258/* Buy with Voucher Button - Cleaner, more compact design */
4259.btn-buy-voucher{
4260  cursor:pointer;border:0;font-weight:600;border-radius:10px;
4261  display:inline-flex;align-items:center;justify-content:space-between;gap:10px;
4262  min-height:44px;padding:0 14px 0 12px;transition:all 0.3s ease;
4263  background:linear-gradient(135deg, #8B6914 0%, #B45309 100%);
4264  color:#fff;
4265  box-shadow:0 3px 10px rgba(124,58,237,0.3);
4266  position:relative;
4267  overflow:hidden;
4268  flex:1 1 auto
4269}
4270.btn-buy-voucher:hover{
4271  transform:translateY(-2px);
4272  box-shadow:0 5px 16px rgba(124,58,237,0.4)
4273}
4274.btn-buy-voucher:active{
4275  transform:translateY(0);
4276  box-shadow:0 2px 6px rgba(124,58,237,0.3)
4277}
4278.btn-buy-voucher .bv-icon{
4279  font-size:20px;
4280  line-height:1;
4281  flex-shrink:0
4282}
4283.btn-buy-voucher .bv-text{
4284  display:flex;
4285  flex-direction:column;
4286  align-items:flex-start;
4287  gap:2px;
4288  flex:1;
4289  min-width:0;
4290  font-size:14px;
4291  line-height:1.2
4292}
4293.btn-buy-voucher .bv-text small{
4294  font-size:10px;
4295  opacity:0.85;
4296  font-weight:500;
4297  letter-spacing:0.3px;
4298  font-family:monospace
4299}
4300.btn-buy-voucher .bv-save{
4301  font-size:12px;
4302  font-weight:700;
4303  background:rgba(255,255,255,0.25);
4304  padding:4px 8px;
4305  border-radius:6px;
4306  white-space:nowrap;
4307  flex-shrink:0
4308}
4309
4310/* Extra meta (opsional) */
4311.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}
4312.pd-extra-meta span{font-weight:600;color:var(--pd-muted);margin-right:.3rem}
4313.pd-extra-meta a{color:var(--pd-accent);text-decoration:none}
4314.pd-extra-meta a:hover{text-decoration:underline}
4315
4316/* === Sidebar === */
4317.pd-sidebar{display:flex;flex-direction:column;gap:1.2rem}
4318.pd-side-card{background:#fff;border:1px solid var(--pd-border);border-radius:var(--pd-radius);padding:1rem 1.1rem 1.2rem}
4319.pd-side-card h3{margin:0 0 .7rem;font-size:.8rem;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--pd-dark)}
4320.pd-side-card ul{list-style:none;margin:0;padding:0;display:grid;gap:.45rem;font-size:.7rem;color:#334155}
4321.pd-side-card li{position:relative;padding-left:13px;line-height:1.25}
4322.pd-side-card li:before{content:'';width:6px;height:6px;background:var(--pd-accent);border-radius:50%;position:absolute;
4322left:0;top:.45rem}
4323.pay-logos{display:flex;gap:.4rem;flex-wrap:wrap;margin:.1rem 0 .4rem}
4324.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}
4325
4326/* Buy card hanya desktop (mobile pakai FAB) */
4327.pd-buy-card{display:block}
4328@media (max-width:880px){ .pd-buy-card{display:none} .pd-title-row {padding-right: 0 !important;}}
4329
4330/* === Full Description === */
4331.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}
4332.pd-full-desc-title{margin:0 0 1.1rem;font-size:1.15rem;font-weight:600;color:var(--pd-dark)}
4333.pd-full-desc-body{font-size:.9rem;line-height:1.6;color:#334155;overflow-wrap:anywhere}
4334.pd-full-desc-body p{margin:0 0 1rem}
4335.pd-full-desc-body ul,.pd-full-desc-body ol{margin:.5rem 0 1rem 1.2rem}
4336.pd-full-desc-body table{width:100%;border-collapse:collapse;margin:1rem 0 1.2rem}
4337.pd-full-desc-body table,.pd-full-desc-body th,.pd-full-desc-body td{border-bottom:1px dotted #cbd5e1;vertical-align: top}
4338.pd-full-desc-body th,.pd-full-desc-body td{padding:.45rem .55rem;text-align:left}
4339.pd-full-desc-body img{max-width:100%;height:auto;display:block;}
4340
4341/* === Related (container) === */
4342.pd-related-full{
4343  background:#fff;border:1px solid var(--pd-border);
4344  border-radius:var(--pd-radius);padding:1.4rem 1.6rem 1.9rem;
4345  margin:0 0 2rem;
4346}
4347.pd-related-title{margin:0 0 1.1rem;font-size:1.05rem;font-weight:600;color:var(--pd-dark)}
4348
4349/* Related Grid (dirapikan: gabung selector agar tidak duplikat) */
4350.pd-related-grid,
4351.pd-related-grid--full{
4352  display:grid !important;
4353  gap:16px !important;
4354  grid-template-columns:repeat(auto-fill,minmax(170px,1fr)) !important;
4355  align-items:stretch;
4356}
4357
4358/* Fallback jika grid tidak didukung */
4359@supports not (display:grid){
4360  .pd-related-grid,
4361  .pd-related-grid--full{display:flex !important;flex-wrap:wrap}
4362  .pd-related-grid .pd-related-card,
4363  .pd-related-grid--full .pd-related-card{flex:0 0 calc(20% - 16px);max-width:calc(20% - 16px)}
4364  @media (max-width:1100px){
4365    .pd-related-grid .pd-related-card,
4366    .pd-related-grid--full .pd-related-card{flex:0 0 calc(25% - 16px);max-width:calc(25% - 16px)}
4367  }
4368  @media (max-width:820px){
4369    .pd-related-grid .pd-related-card,
4370    .pd-related-grid--full .pd-related-card{flex:0 0 calc(33.333% - 16px);max-width:calc(33.333% - 16px)}
4371  }
4372  @media (max-width:580px){
4373    .pd-related-grid .pd-related-card,
4374    .pd-related-grid--full .pd-related-card{flex:0 0 calc(50% - 16px);max-width:calc(50% - 16px)}
4375  }
4376}
4377
4378/* Related Card */
4379.pd-related-card{
4380  display:flex;flex-direction:column;border:1px solid var(--pd-soft-border);
4381  background:#fff;border-radius:10px;text-decoration:none;color:inherit;
4382  overflow:hidden;transition:var(--pd-transition);position:relative;min-width:0;
4383}
4384.pd-related-card:hover{transform:translateY(-4px);box-shadow:var(--pd-shadow)}
4385.pd-related-card .thumb{aspect-ratio:1/1;background:#f1f5f9;overflow:hidden;position:relative}
4386.pd-related-card img{width:100%;height:100%;object-fit:cover;display:block}
4387.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}
4388.pd-related-card .info{padding:.55rem .65rem .75rem;display:flex;flex-direction:column;gap:.25rem}
4389.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}
4390.pd-related-card .price{font-size:.7rem;font-weight:600;color:var(--pd-accent)}
4391.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;}
4392.pd-related-card .price .final {font-size: .82rem; font-weight: 700; color: #0d6efd;}
4393.pd-related-card .price .ori {font-size: .6rem; text-decoration: line-through; color: #94a3b8;font-weight: 500;}
4394
4395/* === Mobile slider (scroll-snap) — gambar utama tetap tampil === */
4396.pd-mobile-slider{display:none}
4397.pd-mobile-slider img{width:100%;display:block;object-fit:contain;aspect-ratio:1/1}
4398
4399/* Gallery counter badge */
4400.pd-gallery-counter{
4401  position:absolute;
4402  bottom:12px;
4403  left:12px;
4404  background:rgba(255,255,255,0.25);
4405  color:#9E9E9E;
4406  padding:4px 10px;
4407  border-radius:10px;
4408  font-size:12px;
4409  font-weight:600;
4410  z-index:100;
4411  backdrop-filter:blur(6px);
4412  -webkit-backdrop-filter:blur(6px);
4413  font-variant-numeric:tabular-nums;
4414  pointer-events:none;
4415  display:none;
4416  box-shadow:0 1px 4px rgba(0,0,0,.15);
4417  line-height:1;
4418  border:1px solid rgba(0,0,0,0.08);
4419}
4420
4421@media (max-width:640px){
4422  .pd-media-card{border: 0;padding: 0 1rem 0;}
4423  .pd-info-card {border: 0; padding: 0 1.3rem 0; }
4424  .pd-side-card {flex: 1 1 !important;}
4425  .pd-top-layout {gap: 0; margin-bottom: 0;}
4426  .pd-variants {margin-bottom: 1.5rem;}
4427  .pd-main-image{display:none}
4428  .pd-gallery-counter{display:block}
4429  .pd-mobile-slider-wrapper{
4430    position:relative;
4431    margin-bottom:.6rem;
4432  }
4433  .pd-mobile-slider{
4434    display:flex;gap:8px;overflow-x:auto;
4435    scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;
4436  }
4437  .pd-mslide{
4438    min-width:100%;flex:0 0 100%;scroll-snap-align:center;
4439    border:1px solid var(--pd-soft-border);border-radius:10px;overflow:hidden;background:#fff
4440  }
4441  .pd-thumb-gallery{display:none} /* hide thumbnails on mobile */
4442}
4443
4444/* === Mobile Floating Action Bar === */
4445.pd-mobile-fab{
4446  position:fixed;
4447  left:0;
4448  right:0;
4449  bottom:42px; /* Position above bottom-nav (60px min-height + 6px padding) */
4450  display:none;
4451  align-items:center;
4452  gap:.75rem;
4453  padding:12px 16px;
4454  background:rgba(255,255,255,0.95);
4455  backdrop-filter:blur(20px);
4456  -webkit-backdrop-filter:blur(20px);
4457  border-top:1px solid rgba(228,231,235,0.6);
4458  box-shadow:0 -4px 16px -4px rgba(0,0,0,0.12);
4459  z-index:999; /* Below bottom-nav (1000) */
4460  transition:bottom .3s cubic-bezier(0.4, 0.0, 0.2, 1);
4461}
4462
4463/* When bottom-nav is hidden, move FAB to bottom */
4464body:has(.bottom-nav.hidden) .pd-mobile-fab {
4465  bottom: 0;
4466}
4467.pd-mobile-fab .mf-actions{display:flex;gap:.5rem;width:100%}
4468.mf-btn{
4469  border-radius:10px;
4470  padding:.6rem 1rem;
4471  font-weight:600;
4472  font-size:14px;
4473  cursor:pointer;
4474  transition:all .2s;
4475  -webkit-tap-highlight-color:transparent;
4476  display:inline-flex;
4477  align-items:center;
4478  justify-content:center;
4479  gap:4px;
4480}
4481.mf-buy{
4482  background:#FBBF24;
4483  color:#fff;
4484  border:0;
4485  box-shadow:0 2px 8px rgba(251, 191, 36, 0.3);
4486  flex:1;
4487  flex-direction:column;
4488  padding:.55rem 1rem;
4489  line-height:1;
4490  min-height:44px;
4491  font-family:inherit;
4492}
4493.mf-buy .mf-buy-text{
4494  font-size:11px;
4495  font-weight:600;
4496  opacity:0.95;
4497  font-family:inherit;
4498}
4499.mf-buy .mf-buy-price{
4500  font-size:13px;
4501  font-weight:800;
4502  letter-spacing:-0.3px;
4503  font-family:inherit;
4504  line-height:1;
4505}
4506.mf-buy:active{transform:scale(0.97)}
4507.mf-cart{
4508  background:#fff;
4509  color:#FBBF24;
4510  border:2px solid #FBBF24;
4511  width:44px;
4512  height:44px;
4513  min-height:44px;
4514  padding:0;
4515  flex-shrink:0;
4516  font-family:inherit;
4517}
4518.mf-cart:active{transform:scale(0.95)}
4519.mf-chat{background:#fff;color:#FBBF24;border:2px solid #FBBF24;width:44px;height:44px;min-height:44px;padding:0;flex-shrink:0;font-family:inherit}
4520.mf-chat:active{transform:scale(0.95)}
4521.mf-chat svg{width:20px;height:20px;flex-shrink:0}
4522.mf-share{
4523  background:#fff;
4524  color:#FBBF24;
4525  border:2px solid #FBBF24;
4526  width:44px;
4527  height:44px;
4528  padding:0;
4529  flex-shrink:0;
4530}
4531.mf-share:active{transform:scale(0.95)}
4532.mf-btn:disabled{opacity:.5;cursor:not-allowed}
4533@media (max-width:640px){
4534  .pd-mobile-fab{display:flex}
4535  .product-single{padding-bottom:0;} /* Space for FAB + bottom-nav */
4536}
4537
4538/* === Installment cards === */
4539.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}
4540.pd-installment-card h3{margin:0 0 .2rem;font-size:.78rem;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:var(--pd-dark)}
4541
4542.inst-tabs{display:flex;gap:.4rem}
4543.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)}
4544.inst-tab.active,.inst-tab:hover{background:var(--pd-accent);color:#fff;border-color:var(--pd-accent)}
4545.inst-tab:focus{outline:2px solid var(--pd-accent);outline-offset:2px}
4546
4547.inst-tenors{display:flex;gap:.4rem;flex-wrap:wrap}
4548.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}
4549.tenor-btn.active,.tenor-btn:hover{background:#1d7df0;color:#fff;border-color:#1d7df0}
4550
4551.inst-result{display:flex;flex-direction:column;gap:.45rem;font-size:.63rem}
4552.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}
4553.inst-line.highlight{background:#1d7df0;color:#fff;border-color:#1d7df0;font-weight:600}
4554.inst-line .lbl{opacity:.8}
4555.inst-line .val{font-weight:600;letter-spacing:.3px}
4556.inst-footnote{font-size:.53rem;line-height:1.25;color:#64748b;margin-top:.2rem}
4557@media (max-width: 860px){
4558  .pd-installment-card{order:3}
4559  .inst-tabs .inst-tab,.inst-tenors .tenor-btn{font-size:.65rem}
4560  
4561  /* Voucher button responsive - make it simpler */
4562  .btn-buy-voucher{
4563    padding:0 12px;
4564    gap:8px
4565  }
4566  .btn-buy-voucher .bv-icon{
4567    font-size:18px
4568  }
4569  .btn-buy-voucher .bv-text{
4570    font-size:13px
4571  }
4572  .btn-buy-voucher .bv-text small{
4573    font-size:9px
4574  }
4575  .btn-buy-voucher .bv-save{
4576    font-size:11px;
4577    padding:3px 6px
4578  }
4579}
4580
4581/* Installment List v2 (eye-catching) */
4582.pd-installment-card2{
4583  background:linear-gradient(135deg,#ffffff 0%,#f1f7ff 65%,#e8f2ff 100%);
4584  border:1px solid var(--pd-border);border-radius:var(--pd-radius);
4585  padding:1rem 1.05rem 1.2rem;display:flex;flex-direction:column;gap:.5rem;
4586  position:relative;overflow:hidden
4587}
4588.pd-installment-card2:before{
4589  content:'';position:absolute;width:140px;height:140px;
4590  background:radial-gradient(circle at center,rgba(29,125,240,.18),rgba(29,125,240,0));
4591  top:-40px;right:-40px;pointer-events:none
4592}
4593.pd-installment-card2 h3{margin:0;font-size:.8rem;font-weight:700;letter-spacing:.6px;text-transform:uppercase;color:#0f2f54}
4594.pd-installment-card2 .inst-head{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
4595.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)}
4596
4597.inst-bank-tabs{display:flex;gap:.45rem}
4598.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}
4599.bank-tab.active,.bank-tab:hover{background:#1d7df0;color:#fff;border-color:#1d7df0;box-shadow:0 3px 10px -4px rgba(29,125,240,.6)}
4600.bank-tab:focus{outline:2px solid #1d7df0;outline-offset:2px}
4601
4602.inst-month-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.55rem}
4603.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}
4604.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}
4605.inst-month-item:hover{transform:translateY(-2px);box-shadow:0 6px 14px -6px rgba(0,40,120,.25)}
4606.inst-month-item:hover:before{opacity:1}
4607.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)}
4608.amount{flex:1;font-size:.8rem;font-weight:600;color:#0f2f54;letter-spacing:.3px}
4609.tag{font-size:.55rem;font-weight:600;padding:.28rem .55rem;border-radius:30px;letter-spacing:.4px;text-transform:uppercase;white-space:nowrap}
4610.tag-zero{background:#16a34a;color:#fff;box-shadow:0 2px 8px -3px rgba(16,180,90,.55)}
4611
4612.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}
4613.inst-disclaimer{font-size:.52rem;line-height:1.25;color:#64748b;margin-top:-.2rem}
4614
4615@media (max-width:860px){
4616  .pd-installment-card2{order:3}
4617  .tenor-badge{min-width:38px;font-size:.75rem}
4618  .amount{font-size:.78rem}
4619}
4620
4621/* Alt Fintech Banner (compact) */
4622.inst-alt-banner--compact{
4623  background:linear-gradient(142deg,#1d7df0 0%,#3c98ff 55%,#67bdff 100%);
4624  color:#fff;border:0;padding:.95rem .95rem 1.05rem;border-radius:16px;
4625  box-shadow:0 6px 18px -6px rgba(0,70,160,.45);position:relative;overflow:hidden;min-height:auto;
4626}
4627.inst-alt-banner--compact:before,
4628.inst-alt-banner--compact:after{
4629  content:'';position:absolute;border-radius:50%;background:rgba(255,255,255,.15);filter:blur(2px);pointer-events:none;
4630}
4631.inst-alt-banner--compact:before{width:130px;height:130px;top:-50px;right:-35px}
4632.inst-alt-banner--compact:after{width:90px;height:90px;bottom:-40px;left:-30px}
4633.inst-alt-banner--compact .iab-top{margin:0 0 .55rem;display:flex;flex-direction:column;gap:.25rem}
4634.inst-alt-banner--compact .iab-title{font-size:.74rem;font-weight:700;letter-spacing:.7px;line-height:1.15}
4635.inst-alt-banner--compact .iab-sub{font-size:.55rem;font-weight:500;opacity:.95;letter-spacing:.4px}
4636
4637.iab-logos-compact{display:flex;gap:.55rem;align-items:center;margin:0 0 .6rem;flex-wrap:nowrap;justify-content:space-between}
4638.iab-logos-compact .logo-fin{width:30%}
4639.iab-logos-compact .logo{
4640  width:48px;height:48px;border-radius:14px;display:flex;align-items:center;justify-content:center;
4641  font-size:.62rem;font-weight:700;letter-spacing:.3px;position:relative;color:#fff;
4642  box-shadow:0 4px 10px -4px rgba(0,0,0,.35);background:rgba(255,255,255,.15);
4643  backdrop-filter:blur(4px);transition:.25s;text-shadow:0 1px 2px rgba(0,0,0,.4);user-select:none;
4644}
4645.iab-logos-compact .logo:before{
4646  content:'';position:absolute;inset:0;border-radius:inherit;
4647  background:linear-gradient(140deg,rgba(255,255,255,.35),rgba(255,255,255,0));mix-blend-mode:overlay;opacity:.55;pointer-events:none;
4648}
4649.iab-logos-compact .logo:hover{transform:translateY(-3px) scale(1.05);box-shadow:0 8px 18px -6px rgba(0,0,0,.45)}
4650
4651.iab-cta--compact{
4652  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;
4653}
4654.iab-cta--compact:hover{transform:translateY(-2px);box-shadow:0 8px 18px -7px rgba(0,0,0,.45)}
4655.iab-footnote--compact{margin-top:.45rem;font-size:.46rem;opacity:.85;letter-spacing:.25px}
4656@media (max-width:860px){
4657  .inst-alt-banner--compact{order:4;margin-top:.4rem;flex:1 1}
4658  .iab-logos-compact .logo{width:46px;height:46px}
4659}
4660
4661/* === Scroll To Top (baru) === */
4662.pd-scroll-top{
4663  position:fixed;
4664  right:20px;
4665  bottom:90px;
4666  width:48px;
4667  height:48px;
4668  border-radius:50%;
4669  background:linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
4670  color:#0f2f54;
4671  border:1px solid rgba(226, 232, 240, 0.8);
4672  box-shadow:0 8px 20px -8px rgba(0,0,0,.3), 0 2px 6px rgba(0,0,0,.1);
4673  display:flex;
4674  align-items:center;
4675  justify-content:center;
4676  cursor:pointer;
4677  z-index:1105;
4678  opacity:0;
4679  transform:translateY(12px) scale(.92);
4680  transition:all .3s cubic-bezier(0.4, 0, 0.2, 1);
4681  backdrop-filter:blur(8px);
4682  -webkit-backdrop-filter:blur(8px);
4683}
4684.pd-scroll-top:hover{
4685  box-shadow:0 12px 28px -10px rgba(0,0,0,.4), 0 4px 12px rgba(0,0,0,.15);
4686  transform:translateY(0) scale(1);
4687  background:linear-gradient(135deg, #ffffff 0%, #fefefe 100%);
4688  border-color:rgba(226, 232, 240, 1);
4689}
4690.pd-scroll-top:active{
4691  transform:translateY(0) scale(.96);
4692}
4693.pd-scroll-top:focus-visible{outline:2px solid var(--pd-accent);outline-offset:2px}
4694.pd-scroll-top.visible{opacity:1;transform:translateY(0) scale(1)}
4695@media (max-width:640px){
4696  /* Dynamic positioning via JavaScript */
4697  .pd-scroll-top{
4698    right:14px;
4699    bottom:var(--scroll-top-bottom, calc(68px + 18px + env(safe-area-inset-bottom)));
4700  }
4701}
4702
4703/* === Responsive layout tweaks === */
4704@media (max-width:1250px){
4705  .pd-top-layout{grid-template-columns:minmax(260px,330px) 1fr}
4706  .pd-sidebar{grid-column:1 / -1;flex-direction:row;flex-wrap:wrap;gap:1rem}
4707  /* Other cards 2-per-row; buy card always takes full row to avoid height-mismatch gaps */
4708  .pd-side-card{flex:1 1 calc(50% - .5rem)}
4709  .pd-buy-card{flex:0 0 100%}
4710}
4711@media (max-width:880px){
4712  .pd-top-layout{grid-template-columns:1fr}
4713  .pd-sidebar{flex-direction:column; padding: 0 1rem;}
4714  .pd-title{font-size:1.2rem}
4715  .pd-price-current{font-size:1.5rem}
4716}
4717@media (max-width:640px){
4718  /* Related: 2 kolom di mobile */
4719  .pd-related-full{padding:1.2rem;border:0;padding-top: .6rem;margin-bottom:.6rem;}
4720  .pd-related-grid,.pd-related-grid--full{grid-template-columns:repeat(2,1fr) !important;gap:12px !important}
4721}
4722@media (max-width:560px){
4723  .pd-actions{flex-direction:column}
4724  .btn-add-cart,.btn-buy,.btn-buy-voucher{width:100%}
4725  .pd-full-description{padding:1.2rem;border:0;padding-bottom:0;}
4726}
4727@media (max-width:380px){
4728  .pd-related-full{padding:.9rem .7rem 1.1rem}
4729  .pd-related-grid,.pd-related-grid--full{gap:8px !important}
4730  .pd-related-card .info{padding:.4rem .48rem .55rem}
4731}
4732
4733/* === Modern Countdown (Product Detail) === */
4734.pd-countdown-modern{
4735  --cd-bg1:#EAB308;          /* gold-500 */
4736  --cd-bg2:#B45309;          /* indigo-500 */
4737  --cd-text:#0b1220;
4738  --cd-contrast:#ffffff;
4739  --cd-shadow: 0 8px 24px -10px rgba(0,0,0,.25);
4740  --cd-border: rgba(255,255,255,.25);
4741  --cd-box: rgba(255,255,255,.65);
4742  --cd-box-text:#0f172a;
4743  --cd-danger:#ef4444;
4744
4745  display:flex; gap:.75rem; align-items:center;
4746  background:linear-gradient(135deg,var(--cd-bg1),var(--cd-bg2));
4747  border:1px solid var(--cd-border);
4748  color:var(--cd-contrast);
4749  border-radius:14px;
4750  padding:.6rem .8rem;
4751  box-shadow:var(--cd-shadow);
4752  position:relative;
4753  overflow:hidden;
4754  margin-bottom: 1rem;
4755}
4756
4757.pd-countdown-modern .pd-cd-icon{
4758  width:28px;height:28px;flex:0 0 28px;
4759  display:grid;place-items:center;
4760  background:rgba(255,255,255,.22);
4761  border:1px solid var(--cd-border);
4762  border-radius:10px;
4763  box-shadow: inset 0 1px 0 rgba(255,255,255,.35);
4764}
4765.pd-countdown-modern .pd-cd-icon svg{width:18px;height:18px;fill:#fff;opacity:.95}
4766
4767.pd-countdown-modern .pd-cd-body{display:flex;flex-direction:column;gap:.35rem;min-width:0}
4768
4769.pd-countdown-modern .pd-cd-label{
4770  font-size:.72rem;font-weight:700;letter-spacing:.4px;opacity:.95;line-height:1;
4771  text-shadow:0 1px 0 rgba(0,0,0,.08);
4772}
4773
4774.pd-countdown-modern .pd-cd-time{
4775  display:flex; align-items:end; gap:.35rem; flex-wrap:wrap;
4776  line-height:1;
4777}
4778
4779.pd-countdown-modern .unit{
4780  display:flex; flex-direction:column; align-items:center; gap:.18rem;
4781  min-width:58px;
4782}
4783.pd-countdown-modern .num{
4784  display:inline-flex; align-items:center; justify-content:center;
4785  min-width:58px; height:40px; padding:0 .4rem;
4786  background:var(--cd-box);
4787  color:var(--cd-box-text);
4788  border:1px solid rgba(15,23,42,.08);
4789  border-radius:10px;
4790  font: 800 1.05rem ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
4791  letter-spacing:.5px;
4792  box-shadow: 0 4px 14px -6px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.55);
4793}
4794.pd-countdown-modern .unit-label{
4795  font-size:.62rem; font-weight:700; opacity:.9; text-shadow:0 1px 0 rgba(255,255,255,.15);
4796}
4797
4798.pd-countdown-modern .colon{
4799  font-weight:900; font-size:1.05rem; opacity:.95; transform: translateY(-2px);
4800  animation: cdBlink 1.2s infinite steps(2, jump-none);
4801}
4802@keyframes cdBlink { 0%{opacity:1} 50%{opacity:.25} 100%{opacity:1} }
4803
4804.pd-countdown-modern .pd-cd-progress{
4805  display:none;
4806  position:relative;
4807  height:6px; border-radius:999px; overflow:hidden;
4808  background:rgba(255,255,255,.25);
4809  border:1px solid rgba(255,255,255,.28);
4810}
4811.pd-countdown-modern .pd-cd-progress .bar{
4812  height:100%; width:0%; background:linear-gradient(90deg,#22c55e,#a3e635);
4813  box-shadow: 0 2px 8px rgba(0,0,0,.25) inset;
4814  transition:width .35s ease;
4815}
4816
4817/* State saat hampir habis */
4818.pd-countdown-modern.danger{
4819  --cd-bg1:#CA8A04; /* gold-600 */
4820  --cd-bg2:#ef4444; /* red-500   */
4821}
4822
4823/* Responsive */
4824@media (max-width:640px){
4825  .pd-countdown-modern{
4826    padding:.55rem .65rem; border-radius:12px; gap:.6rem;
4827  }
4828  .pd-countdown-modern .unit{min-width:52px}
4829  .pd-countdown-modern .num{min-width:52px;height:36px;font-size:.98rem}
4830}
4831
4832/* === Modern Countdown polish (shine + compact) === */
4833.pd-countdown-modern{
4834  position:relative;
4835}
4836.pd-countdown-modern::after{
4837  content:"";
4838  position:absolute; inset:-1px;
4839  background: linear-gradient(120deg, rgba(255,255,255,.12), rgba(255,255,255,0) 35%),
4840              radial-gradient(120% 120% at 0% 0%, rgba(255,255,255,.08), rgba(255,255,255,0) 60%);
4841  border-radius: inherit;
4842  pointer-events:none;
4843  mix-blend-mode: screen;
4844  animation: sheen 4.5s ease-in-out infinite;
4845}
4846@keyframes sheen {
4847  0%   { opacity:.35; transform: translateX(-10%) }
4848  50%  { opacity:.15; transform: translateX(4%) }
4849  100% { opacity:.35; transform: translateX(-10%) }
4850}
4851
4852/* Mode compact (aktifkan dengan data-mode="compact" di elemen) */
4853.pd-countdown-modern[data-mode="compact"]{
4854  gap:.55rem; padding:.5rem .6rem; border-radius:12px;
4855}
4856.pd-countdown-modern[data-mode="compact"] .unit{
4857  min-width:46px;
4858}
4859.pd-countdown-modern[data-mode="compact"] .num{
4860  min-width:46px; height:34px; font-size:.92rem;
4861}
4862.pd-countdown-modern[data-mode="compact"] .pd-cd-label{
4863  font-size:.66rem;
4864}
4865.pd-countdown-modern[data-mode="compact"] .pd-cd-icon{
4866  width:24px; height:24px; border-radius:8px;
4867}
4868.pd-countdown-modern[data-mode="compact"] .pd-cd-progress{
4869  height:5px;
4870}
4871
4872/* ============================================================
4873   Sidebar: shared refinements for medium + tablet (641px – 1250px)
4874   Buy card = form & paylater on the same row.
4875   Alt-fintech logos compact (no over-stretching).
4876   ============================================================ */
4877@media (min-width:641px) and (max-width:1250px) {
4878  /* Buy card: put qty/buy buttons and paylater promo inline */
4879  .pd-buy-card {
4880    display: flex !important;
4881    flex-direction: row;
4882    flex-wrap: wrap;
4883    align-items: center;
4884    gap: .6rem .75rem;
4885  }
4886  .pd-buy-card .pd-buy-form { flex: 1 1 0; min-width: 0; }
4887  .pd-buy-card .paylater-promo {
4888    flex: 0 0 auto;
4889    align-self: center;
4890    margin: 0;
4891    max-width: 44%;
4892  }
4893
4894  /* Alt-fintech banner: tighter logos */
4895  .iab-logos-compact {
4896    justify-content: center;
4897    gap: 1.25rem;
4898  }
4899  .iab-logos-compact .logo-fin {
4900    width: auto;
4901    max-height: 28px;
4902    object-fit: contain;
4903  }
4904}
4905
4906/* ============================================================
4907   Tablet layout (641px – 880px)
4908   Restores 2-column grid + horizontal sidebar for mid-size screens.
4909   Overrides the generic ≤880px mobile-collapse rules above.
4910   ============================================================ */
4911@media (min-width:641px) and (max-width:880px) {
4912  /* 2-column grid: image-card | info-card */
4913  .pd-top-layout {
4914    grid-template-columns: minmax(260px, 40%) 1fr;
4915    gap: 1rem;
4916  }
4917
4918  /* Sidebar: full-width horizontal flex row below the two main columns */
4919  .pd-sidebar {
4920    grid-column: 1 / -1;
4921    flex-direction: row;
4922    flex-wrap: wrap;
4923    gap: .75rem;
4924    padding: 0;
4925  }
4926  /* Banner cards fill 2-per-row */
4927  .pd-side-card { flex: 1 1 calc(50% - .375rem); }
4928  /* Buy card: full row (tallest card – avoids white-space gap if shared with shorter banners) */
4929  .pd-buy-card  { display: block !important; flex: 0 0 100%; }
4930
4931  /* Hide installment simulator + alt-fintech banner on tablet (too much vertical space) */
4932  .pd-installment-card2,
4933  .inst-alt-banner--compact { display: none !important; }
4934
4935  /* Restore sticky add-to-cart header (hidden at ≤880px by default) */
4936  .pd-sticky-header { display: flex !important; }
4937
4938  /* Typography – tighten slightly to fit the narrower info column */
4939  .pd-price-current { font-size: 1.65rem; }
4940  .pd-title         { font-size: 1.1rem;  }
4941  .pd-info-card     { padding: 1.1rem 1.2rem 1.5rem; }
4942}
4943</style>
4944  <style data-inlined="product-grid.css">
4945/* Product Grid (Firebird) */
4946
4947.product-archive {
4948  max-width: 1320px;
4949  margin-inline: auto;
4950  padding: 0 0 3rem;
4951  /*font-family: system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,sans-serif;*/
4952}
4953
4954.pa-toolbar {
4955  display: flex;
4956  flex-wrap: wrap;
4957  gap: 1rem;
4958  justify-content: space-between;
4959  align-items: flex-start;
4960}
4961
4962.pa-title {
4963  font-size: 1.4rem;
4964  margin: 0;
4965  font-weight: 600;
4966  line-height: 1.2;
4967  color: #0f172a;
4968}
4969
4970.pa-left {
4971  display: flex;
4972  flex-wrap: wrap;
4973  gap: .75rem;
4974  align-items: center;
4975}
4976
4977.pa-filter-badge {
4978  background: #f1f5f9;
4979  border: 1px solid #e2e8f0;
4980  padding: .45rem .6rem;
4981  border-radius: 8px;
4982  font-size: .7rem;
4983  display: flex;
4984  align-items: center;
4985  gap: .4rem;
4986  line-height: 1;
4987  color: #475569;
4988}
4989.pa-filter-badge strong { color: #0f172a; }
4990.pa-filter-badge .clear {
4991  display:inline-block;
4992  text-decoration:none;
4993  background:#e2e8f0;
4994  color:#475569;
4995  font-weight:600;
4996  padding:0 .35rem .05rem;
4997  border-radius:4px;
4998  transition:.2s;
4999  font-size:.75rem;
5000  line-height:1;
5001}
5002.pa-filter-badge .clear:hover { background:#cbd5e1; color:#1e293b; }
5003
5004.pa-right {
5005  display:flex;
5006  gap:1rem;
5007  flex-wrap:wrap;
5008  align-items:flex-start;
5009  margin-bottom: 0.5rem;
5010}
5011
5012.pa-search {
5013  display:flex;
5014  gap:.5rem;
5015  align-items:center;
5016  background:#fff;
5017  border:1px solid #e2e8f0;
5018  padding:.4rem .5rem;
5019  border-radius:8px;
5020}
5021.pa-search input[type=text] {
5022  border:0;
5023  outline:none;
5024  font-size:.8rem;
5025  padding:.35rem .4rem;
5026  min-width:200px;
5027}
5028.pa-search button {
5029  background:#0d6efd;
5030  border:0;
5031  color:#fff;
5032  font-size:.7rem;
5033  padding:.45rem .75rem;
5034  border-radius:6px;
5035  cursor:pointer;
5036  font-weight:600;
5037  letter-spacing:.3px;
5038  transition:.25s;
5039}
5040.pa-search button:hover { background:#0b5dc4; }
5041
5042.pa-sort {
5043  display:flex;
5044  gap:.4rem;
5045  align-items:center;
5046  font-size:.7rem;
5047  background:#fff;
5048  border:1px solid #e2e8f0;
5049  padding:.4rem .55rem;
5050  border-radius:8px;
5051}
5052.pa-sort select {
5053  font-size:.75rem;
5054  padding:.35rem .5rem;
5055  border:1px solid #cbd5e1;
5056  border-radius:6px;
5057  background:#fff;
5058  outline:none;
5059}
5060
5061.pa-empty {
5062  border:1px dashed #cbd5e1;
5063  padding:3rem 1rem;
5064  text-align:center;
5065  border-radius:12px;
5066  background:#fff;
5067}
5068.pa-empty p {
5069  margin:0 0 1rem;
5070  font-size:.9rem;
5071  color:#475569;
5072}
5073.btn-reset {
5074  display:inline-block;
5075  background:#0d6efd;
5076  color:#fff;
5077  font-size:.7rem;
5078  padding:.55rem 1rem;
5079  border-radius:8px;
5080  text-decoration:none;
5081  font-weight:600;
5082  letter-spacing:.4px;
5083  transition:.25s;
5084}
5085.btn-reset:hover { background:#0a58c7; }
5086
5087/* Grid */
5088.pa-grid {
5089  display:grid;
5090  gap:1rem;
5091  grid-template-columns:repeat(auto-fill, minmax(180px,1fr));
5092  align-items:stretch;
5093}
5094
5095.pa-card {
5096  background:#fff;
5097  border:1px solid #e2e8f0;
5098  border-radius:12px;
5099  display:flex;
5100  flex-direction:column;
5101  text-decoration:none;
5102  color:inherit;
5103  position:relative;
5104  overflow:hidden;
5105  transition:.28s cubic-bezier(.4,0,.2,1);
5106  min-width:0;
5107  padding: 0;
5108}
5109.pa-card:hover {
5110  transform:translateY(-4px);
5111  box-shadow:0 8px 22px -8px rgba(0,0,0,.15);
5112  border-color:#dce2ea;
5113}
5114
5115.pa-card .thumb {
5116  aspect-ratio:1/1;
5117  background:#f1f5f9;
5118  display:flex;
5119  align-items:center;
5120  justify-content:center;
5121  overflow:hidden;
5122  position:relative;
5123}
5124.pa-card img {
5125  width:100%;
5126  height:100%;
5127  object-fit:cover;
5128  display:block;
5129}
5130.badge-disc {
5131  position:absolute;
5132  top:8px;
5133  left:8px;
5134  background:#dc2626;
5135  color:#fff;
5136  font-size:.6rem;
5137  padding:4px 6px 3px;
5138  border-radius:6px;
5139  font-weight:700;
5140  letter-spacing:.4px;
5141}
5142
5143.pa-card .body {
5144  padding:.65rem .7rem .8rem;
5145  display:flex;
5146  flex-direction:column;
5147  gap:.45rem;
5148  min-height:90px;
5149}
5150
5151.pa-card .title {
5152  font-size:.75rem;
5153  line-height:1.25;
5154  margin:0;
5155  font-weight:600;
5156  color:#1e293b;
5157  height:2.4em;
5158  overflow:hidden;
5159}
5160
5161.pa-card .prices {
5162  display:flex;
5163  flex-wrap:wrap;
5164  align-items:baseline;
5165  gap:.4rem;
5166}
5167.pa-card .prices .final {
5168  font-size:.83rem;
5169  font-weight:700;
5170  color:#0d6efd;
5171}
5172.pa-card .prices .ori {
5173  font-size:.63rem;
5174  text-decoration:line-through;
5175  color:#94a3b8;
5176  font-weight:500;
5177}
5178
5179.pa-card .cat {
5180  margin-top:auto;
5181  font-size:.55rem;
5182  letter-spacing:.6px;
5183  text-transform:uppercase;
5184  background:#f1f5f9;
5185  border:1px solid #e2e8f0;
5186  padding:.28rem .45rem .25rem;
5187  border-radius:20px;
5188  font-weight:600;
5189  color:#475569;
5190  align-self:flex-start;
5191}
5192.pa-card .meta-line {
5193    margin-top:4px;
5194    font-size:.65rem;
5195    color:#475569;
5196}
5197.pa-card .meta-line .sold {
5198    display:inline-block;
5199    background:#f1f5f9;
5200    padding:2px 6px 3px;
5201    border-radius:4px;
5202    font-weight:600;
5203    letter-spacing:.3px;
5204    color:#334155;
5205}
5206
5207/* Pagination */
5208.pa-pagination {
5209  margin-top:2rem;
5210  display:flex;
5211  flex-wrap:wrap;
5212  gap:.4rem;
5213  justify-content:center;
5214  font-size:.7rem;
5215  align-items:center;
5216}
5217.pa-pagination a,
5218.pa-pagination span {
5219  min-width:34px;
5220  height:34px;
5221  display:flex;
5222  align-items:center;
5223  justify-content:center;
5224  border-radius:8px;
5225  text-decoration:none;
5226  font-weight:600;
5227  padding:0 .6rem;
5228  box-sizing:border-box;
5229  border:1px solid #e2e8f0;
5230  background:#fff;
5231  color:#334155;
5232  transition:.25s;
5233}
5234.pa-pagination a:hover {
5235  background:#f1f5f9;
5236  color:#0f172a;
5237}
5238.pa-pagination .current {
5239  background:#0d6efd;
5240  color:#fff;
5241  border-color:#0d6efd;
5242}
5243.pa-pagination .gap {
5244  border:none;
5245  background:transparent;
5246  font-weight:400;
5247  color:#94a3b8;
5248}
5249
5250/* =============== Mobile Filter Bar (Tokopedia-like) =============== */
5251.pa-mobile-filterbar{display:none}
5252@media (max-width: 640px){
5253  /* sembunyikan tombol lama */
5254  .pa-btn-filter{display:none}
5255
5256  .pa-mobile-filterbar{
5257    display:block;
5258    position:sticky;
5259    top:0; /* sesuaikan jika ada header fixed di atas */
5260    z-index:5;
5261    background:#fff;
5262    border-bottom:1px solid #e2e8f0;
5263    padding:.4rem .5rem;
5264    margin:-.4rem -1rem .6rem; /* full-bleed di container mobile */
5265  }
5266  .pa-mobile-filterbar .mf-scroll{
5267    display:flex;align-items:center;gap:.5rem;
5268    overflow-x:auto;
5269    -webkit-overflow-scrolling:touch;
5270    scrollbar-width:none;
5271  }
5272  .pa-mobile-filterbar .mf-scroll::-webkit-scrollbar{display:none}
5273
5274  .mf-chip{
5275    flex:0 0 auto;
5276    background:#fff;
5277    color:#334155;
5278    text-decoration:none;
5279    border:1px solid #e2e8f0;
5280    border-radius:999px;
5281    padding:.44rem .7rem;
5282    font-size:.78rem;
5283    font-weight:600;
5284    letter-spacing:.2px;
5285    white-space:nowrap;
5286  }
5287  .mf-chip:active{transform:scale(.98)}
5288  .mf-chip.active{
5289    background:#eaf2ff;
5290    border-color:#0d6efd;
5291    color:#0d6efd;
5292  }
5293
5294  .mf-filter-btn{
5295    position:relative;
5296    flex:0 0 auto;
5297    width:38px;height:38px;
5298    border-radius:50%;
5299    border:1.5px solid #0d6efd;
5300    background:#fff;
5301    display:inline-flex;align-items:center;justify-content:center;
5302    color:#0d6efd;
5303    cursor:pointer;
5304  }
5305  .mf-filter-btn:active{transform:scale(.96)}
5306  .mf-filter-btn svg{width:18px;height:18px;display:block}
5307  .mf-filter-btn .mf-badge{
5308    position:absolute;top:0;right:-4px;
5309    min-width:18px;height:18px;border-radius:9px;
5310    background:#ef4444;color:#fff;
5311    font-size:.65rem;font-weight:700;
5312    display:flex;align-items:center;justify-content:center;
5313    padding:0 4px;line-height:1;
5314    box-shadow:0 2px 6px -2px rgba(239,68,68,.6);
5315  }
5316}
5317
5318/* Responsive */
5319@media (max-width: 640px) {
5320  .product-archive { padding: 0 1rem 3rem; }
5321  .pa-grid { grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); }
5322  .pa-search input[type=text]{ min-width:140px; }
5323  .pa-toolbar { flex-direction:column; align-items:stretch; margin-bottom: 0; display: none;}
5324  .pa-right { display: none; }
5325  .pa-sort { width: 100%; justify-content:center; }
5326  .pa-mobile-filterbar { display: flex !important; }
5327}
5328
5329.pa-mobile-filterbar {
5330  display: none;
5331  align-items: center;
5332  gap: 8px;
5333  padding: 8px 0 8px 12px;
5334  background: #fff;
5335  width: 100vw;
5336}
5337.pa-mobile-filterbar .mf-scroll {
5338  display: flex;
5339  gap: 8px;
5340  overflow-x: auto;
5341  -webkit-overflow-scrolling: touch;
5342  scrollbar-width: none; /* Firefox */
5343  flex: 1 1 auto; /* takes remaining width */
5344}
5345.pa-mobile-filterbar .mf-scroll::-webkit-scrollbar { display: none; } /* WebKit */
5346.pa-mobile-filterbar .mf-chip {
5347  flex: 0 0 auto; /* prevent shrinking, keep width based on content */
5348  white-space: nowrap;
5349}
5350.pa-mobile-filterbar .mf-filter-btn {
5351  flex: 0 0 auto; /* do not shrink */
5352  position: relative; /* keep in place, not inside scroller */
5353  display: inline-flex;
5354  align-items: center;
5355  gap: 6px;
5356}
5357
5358/* Optional: ensure on small screens the button stays visible with min width */
5359@media (max-width: 768px) {
5360  .pa-mobile-filterbar .mf-filter-btn {
5361    padding: 6px 10px;
5362  }
5363}
5364
5365/* Product Specification Chips */
5366.pa-card .spec-chips {
5367  font-size: .6rem;
5368  color: #64748b;
5369  font-weight: 500;
5370  letter-spacing: .2px;
5371  margin-top: 2px;
5372  margin-bottom: 4px;
5373  line-height: 1.2;
5374  opacity: 0.9;
5375  max-width: 100%;
5376  overflow: hidden;
5377  text-overflow: ellipsis;
5378  white-space: nowrap;
5379}
5380
5381.pa-card .spec-chips .spec-divider {
5382  color: #94a3b8;
5383  font-weight: 400;
5384  margin: 0 1px;
5385}
5386</style>
5387  <style data-inlined="site-notice.css">
5388/**
5389 * Site Notice Banner - Reusable Notification System
5390 * Dismissible top bar for announcements, holidays, maintenance, etc.
5391 */
5392
5393.holiday-notice {
5394    position: fixed;
5395    top: 0;
5396    left: 0;
5397    right: 0;
5398    z-index: 9999;
5399    background: linear-gradient(135deg, #d32f2f 0%, #8B6914 100%);
5400    border-bottom: 2px solid rgba(255, 255, 255, 0.2);
5401    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
5402    animation: slideDown 0.4s cubic-bezier(0.4, 0, 0.2, 1);
5403    transform-origin: top;
5404}
5405
5406.holiday-notice.hidden {
5407    animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
5408}
5409
5410@keyframes slideDown {
5411    from {
5412        transform: translateY(-100%);
5413        opacity: 0;
5414    }
5415    to {
5416        transform: translateY(0);
5417        opacity: 1;
5418    }
5419}
5420
5421@keyframes slideUp {
5422    from {
5423        transform: translateY(0);
5424        opacity: 1;
5425    }
5426    to {
5427        transform: translateY(-100%);
5428        opacity: 0;
5429    }
5430}
5431
5432.holiday-notice-container {
5433    max-width: 1400px;
5434    margin: 0 auto;
5435    padding: 12px 20px;
5436    display: flex;
5437    align-items: center;
5438    justify-content: space-between;
5439    gap: 16px;
5440}
5441
5442.holiday-notice-content {
5443    display: flex;
5444    align-items: center;
5445    gap: 12px;
5446    flex: 1;
5447}
5448
5449.holiday-notice-icon {
5450    font-size: 20px;
5451    line-height: 1;
5452    flex-shrink: 0;
5453}
5454
5455.holiday-notice-text {
5456    color: #ffffff;
5457    font-size: 14px;
5458    font-weight: 500;
5459    line-height: 1.5;
5460    margin: 0;
5461    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
5462}
5463
5464.holiday-notice-emoji {
5465    display: inline-block;
5466    animation: bounce 2s ease-in-out infinite;
5467}
5468
5469@keyframes bounce {
5470    0%, 100% {
5471        transform: translateY(0);
5472    }
5473    50% {
5474        transform: translateY(-4px);
5475    }
5476}
5477
5478.holiday-notice-close {
5479    background: rgba(255, 255, 255, 0.2);
5480    border: 1px solid rgba(255, 255, 255, 0.3);
5481    border-radius: 6px;
5482    padding: 6px;
5483    cursor: pointer;
5484    color: #ffffff;
5485    transition: all 0.2s ease;
5486    flex-shrink: 0;
5487    display: flex;
5488    align-items: center;
5489    justify-content: center;
5490    width: 32px;
5491    height: 32px;
5492}
5493
5494.holiday-notice-close:hover {
5495    background: rgba(255, 255, 255, 0.3);
5496    transform: scale(1.05);
5497}
5498
5499.holiday-notice-close:active {
5500    transform: scale(0.95);
5501}
5502
5503.holiday-notice-close:focus {
5504    outline: 2px solid rgba(255, 255, 255, 0.5);
5505    outline-offset: 2px;
5506}
5507
5508/* Body padding adjustment when banner is visible */
5509body.has-holiday-notice {
5510    padding-top: 52px;
5511}
5512
5513/* Tablet Styles */
5514@media (max-width: 1024px) {
5515    .holiday-notice-container {
5516        padding: 10px 16px;
5517    }
5518    
5519    .holiday-notice-text {
5520        font-size: 13px;
5521    }
5522    
5523    .holiday-notice-icon {
5524        font-size: 18px;
5525    }
5526    
5527    body.has-holiday-notice {
5528        padding-top: 48px;
5529    }
5530}
5531
5532/* Mobile Styles */
5533@media (max-width: 640px) {
5534    .holiday-notice-container {
5535        padding: 10px 12px;
5536        gap: 8px;
5537    }
5538    
5539    .holiday-notice-content {
5540        gap: 8px;
5541    }
5542    
5543    .holiday-notice-text {
5544        font-size: 12px;
5545        line-height: 1.4;
5546    }
5547    
5548    .holiday-notice-icon {
5549        font-size: 16px;
5550    }
5551    
5552    .holiday-notice-close {
5553        width: 28px;
5554        height: 28px;
5555    }
5556    
5557    .holiday-notice-close svg {
5558        width: 16px;
5559        height: 16px;
5560    }
5561    
5562    body.has-holiday-notice {
5563        padding-top: 44px;
5564    }
5565}
5566
5567/* Ensure header doesn't get covered - removed z-index as it conflicts with dropdown stacking */
5568/* .header,
5569.navbar,
5570header {
5571    position: relative;
5572    z-index: 100;
5573} */
5574
5575</style>
5576        <style data-inlined="benchmark.css">
5577/**
5578 * Benchmark Section Styles
5579 * For product detail page benchmark display
5580 */
5581
5582.pd-benchmark-section {
5583    background: #fff;
5584    border-radius: 12px;
5585    padding: 24px;
5586    margin: 32px 0;
5587    box-shadow: 0 2px 8px rgba(0,0,0,0.06);
5588    border: 1px solid #e5e7eb;
5589}
5590
5591.benchmark-header {
5592    margin-bottom: 20px;
5593}
5594
5595.benchmark-title {
5596    font-size: 22px;
5597    font-weight: 700;
5598    color: #1a1a1a;
5599    margin: 0 0 12px 0;
5600    display: flex;
5601    align-items: center;
5602    gap: 8px;
5603}
5604
5605.benchmark-hint {
5606    font-size: 14px;
5607    color: #6b7280;
5608    margin: 0;
5609}
5610
5611/* Preview Badges */
5612.benchmark-preview-badges {
5613    display: flex;
5614    flex-wrap: wrap;
5615    gap: 10px;
5616    margin-top: 12px;
5617}
5618
5619.benchmark-badge {
5620    display: inline-flex;
5621    align-items: center;
5622    gap: 6px;
5623    background: linear-gradient(135deg, #EAB308 0%, #8B6914 100%);
5624    color: #fff;
5625    padding: 8px 16px;
5626    border-radius: 20px;
5627    font-size: 13px;
5628    font-weight: 500;
5629    box-shadow: 0 2px 6px rgba(102, 126, 234, 0.25);
5630    transition: all 0.2s ease;
5631}
5632
5633.benchmark-badge:hover {
5634    transform: translateY(-2px);
5635    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.35);
5636}
5637
5638.benchmark-badge strong {
5639    font-weight: 600;
5640    opacity: 0.9;
5641}
5642
5643/* Load Button */
5644.btn-load-benchmark {
5645    display: flex;
5646    align-items: center;
5647    justify-content: center;
5648    gap: 10px;
5649    width: 100%;
5650    max-width: 420px;
5651    padding: 14px 24px;
5652    background: linear-gradient(135deg, #EAB308 0%, #8B6914 100%);
5653    color: #fff;
5654    border: none;
5655    border-radius: 8px;
5656    font-size: 15px;
5657    font-weight: 600;
5658    cursor: pointer;
5659    transition: all 0.3s ease;
5660    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
5661    margin: 0 auto;
5662}
5663
5664.btn-load-benchmark:hover:not(:disabled) {
5665    transform: translateY(-2px);
5666    box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
5667}
5668
5669.btn-load-benchmark:disabled {
5670    opacity: 0.6;
5671    cursor: not-allowed;
5672}
5673
5674.btn-load-benchmark .btn-icon {
5675    font-size: 20px;
5676}
5677
5678.btn-load-benchmark.loading .btn-icon {
5679    animation: spin 1s linear infinite;
5680}
5681
5682@keyframes spin {
5683    from { transform: rotate(0deg); }
5684    to { transform: rotate(360deg); }
5685}
5686
5687/* Benchmark Content */
5688.benchmark-content {
5689    margin-top: 24px;
5690    padding-top: 24px;
5691    border-top: 2px solid #f3f4f6;
5692}
5693
5694.benchmark-loading {
5695    text-align: center;
5696    padding: 40px 20px;
5697    color: #6b7280;
5698}
5699
5700.benchmark-loading-spinner {
5701    width: 48px;
5702    height: 48px;
5703    margin: 0 auto 16px;
5704    border: 4px solid #f3f4f6;
5705    border-top-color: #EAB308;
5706    border-radius: 50%;
5707    animation: spin 1s linear infinite;
5708}
5709
5710.benchmark-error {
5711    text-align: center;
5712    padding: 32px 20px;
5713    color: #ef4444;
5714    background: #fee2e2;
5715    border-radius: 8px;
5716}
5717
5718/* Benchmark Data Display */
5719.benchmark-data-grid {
5720    display: grid;
5721    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
5722    gap: 16px;
5723    margin-bottom: 24px;
5724}
5725
5726.benchmark-card {
5727    background: linear-gradient(135deg, #EAB30815 0%, #8B691415 100%);
5728    border: 1px solid #e5e7eb;
5729    border-radius: 10px;
5730    padding: 18px;
5731    text-align: center;
5732    transition: all 0.2s ease;
5733}
5734
5735.benchmark-card:hover {
5736    transform: translateY(-3px);
5737    box-shadow: 0 6px 16px rgba(102, 126, 234, 0.15);
5738}
5739
5740.benchmark-card-label {
5741    font-size: 13px;
5742    color: #6b7280;
5743    font-weight: 500;
5744    margin-bottom: 8px;
5745    text-transform: uppercase;
5746    letter-spacing: 0.5px;
5747}
5748
5749.benchmark-card-value {
5750    font-size: 24px;
5751    font-weight: 700;
5752    color: #1a1a1a;
5753    line-height: 1.2;
5754}
5755
5756.benchmark-card-value.large {
5757    font-size: 28px;
5758}
5759
5760.benchmark-card-subtitle {
5761    font-size: 12px;
5762    color: #9ca3af;
5763    margin-top: 4px;
5764}
5765
5766/* Spec Details */
5767.benchmark-specs {
5768    background: #f9fafb;
5769    border-radius: 10px;
5770    padding: 20px;
5771    margin-top: 20px;
5772}
5773
5774.benchmark-specs-title {
5775    font-size: 16px;
5776    font-weight: 600;
5777    color: #1a1a1a;
5778    margin: 0 0 16px 0;
5779}
5780
5781.benchmark-spec-row {
5782    display: flex;
5783    justify-content: space-between;
5784    padding: 10px 0;
5785    border-bottom: 1px solid #e5e7eb;
5786}
5787
5788.benchmark-spec-row:last-child {
5789    border-bottom: none;
5790}
5791
5792.benchmark-spec-label {
5793    font-size: 14px;
5794    color: #6b7280;
5795    font-weight: 500;
5796}
5797
5798.benchmark-spec-value {
5799    font-size: 14px;
5800    color: #1a1a1a;
5801    font-weight: 600;
5802    text-align: right;
5803}
5804
5805/* Score Badges */
5806.benchmark-score-badge {
5807    display: inline-block;
5808    padding: 6px 14px;
5809    border-radius: 20px;
5810    font-size: 13px;
5811    font-weight: 600;
5812    margin-top: 8px;
5813}
5814
5815.benchmark-score-badge.excellent {
5816    background: #10b981;
5817    color: #fff;
5818}
5819
5820.benchmark-score-badge.good {
5821    background: #EAB308;
5822    color: #fff;
5823}
5824
5825.benchmark-score-badge.average {
5826    background: #CA8A04;
5827    color: #fff;
5828}
5829
5830.benchmark-score-badge.poor {
5831    background: #ef4444;
5832    color: #fff;
5833}
5834
5835/* Attribution */
5836.benchmark-attribution {
5837    text-align: center;
5838    margin-top: 24px;
5839    padding-top: 20px;
5840    border-top: 1px solid #e5e7eb;
5841    font-size: 13px;
5842    color: #9ca3af;
5843}
5844
5845.benchmark-attribution a {
5846    color: #EAB308;
5847    text-decoration: none;
5848    font-weight: 500;
5849}
5850
5851.benchmark-attribution a:hover {
5852    text-decoration: underline;
5853}
5854
5855/* Responsive */
5856@media (max-width: 768px) {
5857    .pd-benchmark-section {
5858        padding: 16px;
5859        margin: 20px 0;
5860    }
5861    
5862    .benchmark-title {
5863        font-size: 18px;
5864    }
5865    
5866    .benchmark-preview-badges {
5867        flex-direction: column;
5868        gap: 8px;
5869    }
5870    
5871    .benchmark-badge {
5872        justify-content: center;
5873    }
5874    
5875    .btn-load-benchmark {
5876        font-size: 14px;
5877        padding: 12px 20px;
5878    }
5879    
5880    .benchmark-data-grid {
5881        grid-template-columns: 1fr 1fr;
5882        gap: 12px;
5883    }
5884    
5885    .benchmark-card {
5886        padding: 14px;
5887    }
5888    
5889    .benchmark-card-value {
5890        font-size: 20px;
5891    }
5892    
5893    .benchmark-spec-row {
5894        flex-direction: column;
5895        gap: 4px;
5896    }
5897    
5898    .benchmark-spec-value {
5899        text-align: left;
5900    }
5901}
5902
5903@media (max-width: 480px) {
5904    .benchmark-data-grid {
5905        grid-template-columns: 1fr;
5906    }
5907}
5908
5909</style>
5910    <style data-inlined="product-filters.css">
5911/* =========================================================
5912   Product Filters (Desktop + Mobile Drawer)
5913   Light Only + FIX:
5914   - Desktop: f-actions selalu tampil (static bottom)
5915   - Mobile: konten filter dapat discroll
5916   ========================================================= */
5917
5918/* ========== Layout wrapper (tidak mengubah grid produk) ========== */
5919.product-archive.with-filters .pa-layout {
5920  display:flex;
5921  gap:24px;
5922  align-items:flex-start;
5923}
5924.pa-main { flex:1; min-width:0; }
5925
5926/* =========================================================
5927   DESKTOP PANEL
5928   ========================================================= */
5929.pa-filters {
5930  width:300px;
5931  background:linear-gradient(145deg,#ffffff 0%,#f8fafc 55%,#f1f5f9 100%);
5932  border:1px solid #e2e8f0;
5933  padding:18px 18px 12px; /* bottom dikurangi karena f-actions punya padding sendiri */
5934  border-radius:18px;
5935  position:sticky;
5936  top:80px;
5937  height:calc(100vh - 100px); /* tinggi stabil untuk inner scroll */
5938  font-size:14px;
5939  box-shadow:0 1px 2px rgba(0,0,0,.04),0 4px 14px rgba(15,23,42,.08);
5940  backdrop-filter:blur(3px);
5941  -webkit-backdrop-filter:blur(3px);
5942  transition:box-shadow .3s, background .4s;
5943  display:flex;
5944  flex-direction:column;
5945  overflow:hidden; /* cegah double scroll, inner yang scroll */
5946}
5947.pa-filters::before {
5948  content:"";
5949  position:absolute;
5950  inset:0 auto 0 0;
5951  width:7px;
5952  border-radius:18px 0 0 18px;
5953  background:linear-gradient(180deg,#CA8A04 0%,#FBBF24 35%,#C9A227 100%);
5954}
5955.pa-filters.active { box-shadow:0 8px 32px -4px rgba(15,23,42,.35); }
5956
5957/* Scroll area (desktop/mode shared) */
5958.filters-scroll {
5959  flex:1;
5960  min-height:0;          /* wajib agar overflow terbaca dalam flex container */
5961  overflow:auto;
5962  padding-right:6px;
5963  margin-right:-6px;      /* kompensasi scrollbar */
5964  scrollbar-width:thin;
5965  overscroll-behavior:contain;
5966}
5967.filters-scroll::-webkit-scrollbar { width:8px; }
5968.filters-scroll::-webkit-scrollbar-thumb {
5969  background:#cbd5e1;
5970  border-radius:20px;
5971  border:2px solid #f1f5f9;
5972}
5973.filters-scroll::-webkit-scrollbar-thumb:hover { background:#94a3b8; }
5974
5975/* Heading */
5976.pa-filters h2 {
5977  margin:0 0 14px;
5978  font-size:20px;
5979  font-weight:700;
5980  letter-spacing:.4px;
5981  line-height:1.15;
5982  background:linear-gradient(90deg,#0f172a,#C9A227 60%,#0f172a);
5983  -webkit-background-clip:text;
5984  color:transparent;
5985  background-size:200% 100%;
5986  animation:titleSheen 8s linear infinite;
5987}
5988@keyframes titleSheen {
5989  0%{background-position:0 0}
5990  50%{background-position:100% 0}
5991  100%{background-position:0 0}
5992}
5993
5994/* Form groups */
5995.pa-filters .f-group { margin-bottom:18px; position:relative; }
5996.pa-filters label {
5997  display:block;
5998  font-weight:600;
5999  margin:0 0 6px;
6000  font-size:13px;
6001  color:#0f172a;
6002  letter-spacing:.3px;
6003}
6004
6005/* Inputs / selects */
6006.pa-filters input[type=text],
6007.pa-filters input[type=number],
6008.pa-filters select {
6009  width:100%;
6010  padding:7px 10px;
6011  border:1px solid #cbd5e1;
6012  border-radius:8px;
6013  font-size:13px;
6014  min-height:34px;
6015  background:#ffffff;
6016  box-sizing:border-box;
6017  color:#0f172a;
6018  transition:border-color .25s, box-shadow .25s, background .25s;
6019  -webkit-appearance:none;
6020  -moz-appearance:none;
6021  appearance:none;
6022}
6023.pa-filters select {
6024  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");
6025  background-repeat:no-repeat;
6026  background-position:right 10px center;
6027  background-size:12px;
6028  padding-right:32px;
6029}
6030.pa-filters input[type=text]:hover,
6031.pa-filters input[type=number]:hover,
6032.pa-filters select:hover { border-color:#94b5ec; }
6033.pa-filters input[type=text]:focus,
6034.pa-filters input[type=number]:focus,
6035.pa-filters select:focus {
6036  outline:none;
6037  border-color:#C9A227;
6038  box-shadow:0 0 0 3px rgba(201,162,39,.25);
6039  background:#f8fbff;
6040}
6041.pa-filters .range { display:flex; gap:10px; }
6042.pa-filters .range input { flex:1; }
6043.pa-filters .hint { font-size:11px; color:#64748b; margin-top:6px; }
6044
6045/* Spec blocks */
6046.f-specs { margin-top:4px; }
6047.spec-attr {
6048  margin:0 0 14px;
6049  padding:12px 12px 10px;
6050  border:1px solid #e2e8f0;
6051  border-radius:14px;
6052  background:linear-gradient(130deg,#ffffff 0%,#f7faff 85%);
6053  position:relative;
6054  transition:border-color .3s, box-shadow .3s, background .4s;
6055}
6056.spec-attr.collapsed { padding-bottom:8px; }
6057.spec-attr.has-selected {
6058  border-color:#C9A227;
6059  box-shadow:0 0 0 2px rgba(201,162,39,.15),0 2px 8px -2px rgba(201,162,39,.35);
6060  background:linear-gradient(135deg,#ffffff 0%,#e9f2ff 100%);
6061}
6062.spec-attr-title {
6063  font-weight:600;
6064  margin:0 0 8px;
6065  font-size:13px;
6066  display:flex;
6067  justify-content:space-between;
6068  align-items:center;
6069  cursor:pointer;
6070  color:#0f172a;
6071  user-select:none;
6072}
6073.spec-attr-title .chevron {
6074  font-size:10px;
6075  transition:transform .25s, opacity .25s;
6076  opacity:.65;
6077}
6078.spec-attr.collapsed .spec-attr-title .chevron {
6079  transform:rotate(-90deg);
6080  opacity:.5;
6081}
6082
6083/* Spec options */
6084.spec-attr-options { display:flex; flex-wrap:wrap; gap:6px; }
6085.spec-attr-options label {
6086  display:inline-flex;
6087  align-items:center;
6088  gap:4px;
6089  font-weight:500;
6090  border:1px solid #d4dde7;
6091  padding:6px 9px;
6092  border-radius:30px;
6093  cursor:pointer;
6094  background:#f3f6fa;
6095  font-size:12px;
6096  line-height:1.1;
6097  transition:background .25s, border-color .25s, color .25s, box-shadow .25s;
6098  position:relative;
6099}
6100.spec-attr-options label:hover { background:#e8f1ff; border-color:#b1cff3; }
6101.spec-attr-options input {
6102  margin:0;
6103  position:absolute;
6104  inset:0;
6105  opacity:0;
6106  cursor:pointer;
6107}
6108.spec-attr-options label:has(input:checked) {
6109  background:linear-gradient(135deg,#C9A227 0%,#A16207 100%);
6110  border-color:#C9A227;
6111  color:#ffffff;
6112  box-shadow:0 2px 6px -1px rgba(201,162,39,.55);
6113}
6114.spec-attr-options label:has(input:checked)::after {
6115  content:"";
6116  position:absolute;
6117  right:6px;
6118  top:6px;
6119  width:6px;
6120  height:6px;
6121  background:#fff;
6122  border-radius:50%;
6123  box-shadow:0 0 0 4px rgba(255,255,255,.25),0 0 0 7px rgba(201,162,39,.5);
6124  animation:pulseDot 1.9s ease-out infinite;
6125}
6126@keyframes pulseDot {
6127  0%{transform:scale(.8);opacity:.9}
6128  60%{transform:scale(1.4);opacity:.15}
6129  100%{transform:scale(.8);opacity:.9}
6130}
6131
6132/* Badges */
6133.pa-filter-badges { margin:0 0 18px; display:flex; flex-wrap:wrap; gap:6px; }
6134.pa-filter-badge {
6135  display:inline-flex;
6136  gap:6px;
6137  align-items:center;
6138  background:linear-gradient(135deg,#eef4ff 0%,#e0edff 100%);
6139  border:1px solid #b4cff3;
6140  padding:5px 10px 5px 12px;
6141  border-radius:22px;
6142  margin:2px 4px 0 0;
6143  font-size:11px;
6144  font-weight:500;
6145  line-height:1;
6146  color:#0f172a;
6147  position:relative;
6148  overflow:hidden;
6149}
6150.pa-filter-badge::before {
6151  content:"";
6152  position:absolute;
6153  inset:0;
6154  background:linear-gradient(90deg,rgba(255,255,255,0),rgba(255,255,255,.35),rgba(255,255,255,0));
6155  transform:translateX(-100%);
6156  animation:badgeSheen 6s linear infinite;
6157}
6158@keyframes badgeSheen {
6159  0%{transform:translateX(-100%)}
6160  55%{transform:translateX(120%)}
6161  100%{transform:translateX(120%)}
6162}
6163.pa-filter-badge a.clear {
6164  text-decoration:none;
6165  color:#c02828;
6166  font-weight:700;
6167  padding:0 3px;
6168  display:inline-flex;
6169  align-items:center;
6170  justify-content:center;
6171  border-radius:50%;
6172  line-height:1;
6173  transition:background .25s, color .25s;
6174}
6175.pa-filter-badge a.clear:hover { background:#ffeded; color:#a40000; }
6176.pa-btn-filter { width: 100%;}
6177
6178/* =========================================================
6179   ACTIONS (DESKTOP) - STATIC BOTTOM
6180   ========================================================= */
6181.f-actions {
6182  margin-top:10px;
6183  display:flex;
6184  gap:12px;
6185  flex-wrap:wrap;
6186  padding:16px 0 4px;
6187  border-top:1px solid #e2e8f0;
6188  background:linear-gradient(180deg,rgba(255,255,255,0) 0%,#f1f5f9 70%);
6189  flex-shrink:0;
6190}
6191.f-actions button {
6192  background:linear-gradient(135deg,#C9A227 0%,#A16207 100%);
6193  color:#fff;
6194  border:none;
6195  padding:10px 20px;
6196  border-radius:12px;
6197  cursor:pointer;
6198  font-size:14px;
6199  font-weight:600;
6200  letter-spacing:.3px;
6201  display:inline-flex;
6202  align-items:center;
6203  gap:6px;
6204  box-shadow:0 4px 14px -2px rgba(201,162,39,.45);
6205  transition:transform .25s, box-shadow .3s;
6206}
6207.f-actions button:hover { transform:translateY(-3px); box-shadow:0 8px 26px -4px rgba(201,162,39,.6); }
6208.f-actions button:active { transform:translateY(-1px) scale(.98); box-shadow:0 4px 14px -4px rgba(201,162,39,.55); }
6209.f-actions .reset {
6210  font-size:12px;
6211  text-decoration:none;
6212  color:#d92d2d;
6213  align-self:center;
6214  font-weight:600;
6215  padding:4px 6px;
6216  border-radius:8px;
6217  transition:background .25s, color .25s;
6218}
6219.f-actions .reset:hover { background:#ffe6e6; color:#b80000; text-decoration:underline; }
6220.pa-filters .close-drawer { display: none; }
6221
6222/* Form wrapper: flex container agar inner .filters-scroll bisa scroll di desktop */
6223.filter-form {
6224  flex: 1;
6225  min-height: 0;
6226  display: flex;
6227  flex-direction: column;
6228  overflow: hidden;
6229}
6230
6231/* =========================================================
6232   FILTER TOGGLE (MOBILE TRIGGER)
6233   ========================================================= */
6234.filter-toggle {
6235  display:none;
6236  margin:0 0 14px;
6237  background:linear-gradient(135deg,#C9A227 0%,#A16207 100%);
6238  color:#fff;
6239  border:none;
6240  padding:11px 18px;
6241  font-size:14px;
6242  font-weight:600;
6243  border-radius:14px;
6244  cursor:pointer;
6245  letter-spacing:.4px;
6246  box-shadow:0 4px 14px -2px rgba(201,162,39,.55);
6247  position:relative;
6248  z-index:100;
6249  transition:box-shadow .3s, transform .25s;
6250}
6251.filter-toggle:hover { box-shadow:0 8px 26px -4px rgba(201,162,39,.6); transform:translateY(-2px); }
6252.filter-toggle:active { transform:translateY(0); box-shadow:0 4px 14px -4px rgba(201,162,39,.55); }
6253.filter-toggle .count-badge {
6254  display:inline-block;
6255  background:linear-gradient(135deg,#CA8A04 0%,#FBBF24 55%,#CA8A04 100%);
6256  color:#1d2b44;
6257  font-size:11px;
6258  font-weight:700;
6259  padding:3px 7px 4px;
6260  border-radius:30px;
6261  margin-left:8px;
6262  box-shadow:0 0 0 2px #ffffff,0 0 0 4px #C9A227;
6263}
6264
6265/* Overlay (mobile) */
6266.pa-filters-overlay { display:none; }
6267
6268/* =========================================================
6269   MOBILE DRAWER
6270   ========================================================= */
6271@media (max-width:768px){
6272  .product-archive.with-filters .pa-layout { flex-direction:column; gap:12px; }
6273  .filter-toggle { display:inline-flex; align-items:center; gap:6px; }
6274
6275  .pa-filters-overlay {
6276    position:fixed;
6277    inset:0;
6278    background:rgba(0,20,45,.55);
6279    z-index:998;
6280    opacity:0;
6281    transition:opacity .35s;
6282    display:block;
6283    pointer-events:none;
6284    backdrop-filter:blur(2px);
6285  }
6286  .pa-filters-overlay.active { opacity:1; pointer-events:auto; }
6287
6288  .pa-filters {
6289    position:fixed;
6290    top:auto;
6291    left:0; right:0; bottom:0;
6292    max-height:92vh;
6293    height:auto;
6294    width:100%;
6295    border-radius:30px 30px 0 0;
6296    border:1px solid #dbe4ef;
6297    padding:26px 22px 0;
6298    z-index:10000; /* di atas bottom-nav (9998) */
6299    transform:translateY(102%);
6300    transition:transform .55s cubic-bezier(.68,-0.35,.25,1);
6301    box-shadow:0 -4px 40px -6px rgba(15,23,42,.45);
6302    overflow:hidden; /* outer hidden, inner scroll */
6303  }
6304  .pa-filters.active { transform:translateY(0); }
6305
6306  /* Drag handle indicator */
6307  .pa-filters::after {
6308    content:"";
6309    display:block;
6310    position:absolute;
6311    top:10px;
6312    left:50%;
6313    transform:translateX(-50%);
6314    width:36px;
6315    height:4px;
6316    background:#cbd5e1;
6317    border-radius:4px;
6318  }
6319  body.filter-open { overflow:hidden; touch-action:none; }
6320
6321  /* Restore mobile-friendly input sizes (prevent iOS zoom on focus) */
6322  .pa-filters input[type=text],
6323  .pa-filters input[type=number],
6324  .pa-filters select {
6325    font-size:16px;
6326    min-height:44px;
6327    padding:10px 12px;
6328    border-radius:10px;
6329  }
6330  .pa-filters select { padding-right:32px; }
6331
6332  .filters-scroll {
6333    flex:1;
6334    overflow:auto;
6335    padding:0 0 120px; /* ruang di atas bar actions */
6336    margin:0;
6337    -webkit-overflow-scrolling:touch;
6338  }
6339
6340  .pa-filters h2 { font-size:19px; margin-bottom:10px; }
6341
6342  .pa-filters .close-drawer {
6343    display: block;
6344    position:absolute;
6345    right:14px; top:12px;
6346    background:linear-gradient(135deg,#dc2626 0%,#b91c1c 100%);
6347    color:#fff;
6348    padding:7px 14px;
6349    border:none;
6350    border-radius:10px;
6351    font-size:12px;
6352    font-weight:600;
6353    cursor:pointer;
6354    box-shadow:0 3px 10px -2px rgba(220,38,38,.55);
6355    transition:transform .25s, box-shadow .3s;
6356    z-index: 99;
6357  }
6358  .pa-filters .close-drawer:hover { transform:translateY(-2px); box-shadow:0 6px 18px -4px rgba(220,38,38,.6); }
6359
6360  .spec-attr { border-radius:18px; }
6361  .spec-attr-options label { font-size:11px; padding:5px 9px; }
6362
6363  /* Actions fixed di viewport bawah */
6364  .f-actions {
6365    position:fixed;
6366    left:0; right:0; bottom:0;
6367    margin:0;
6368    border-top:1px solid #d6dee8;
6369    padding:14px 16px calc(16px + env(safe-area-inset-bottom));
6370    background:#ffffffea;
6371    backdrop-filter:blur(6px);
6372    box-shadow:0 -4px 22px -6px rgba(15,23,42,.25);
6373    justify-content:space-between;
6374    z-index:10001; /* di atas pa-filters (10000) dan bottom-nav (9998) */
6375  }
6376
6377  /* Sembunyikan bottom-nav saat filter drawer terbuka */
6378  body.filter-open .bottom-nav {
6379    display: none !important;
6380  }
6381  .f-actions button {
6382    flex:1 1 auto;
6383    justify-content:center;
6384    padding:14px 14px;
6385    font-size:15px;
6386    border-radius:14px;
6387  }
6388  .f-actions .reset {
6389    order:-1;
6390    font-size:12px;
6391    padding:2px 8px;
6392  }
6393}
6394
6395/* Scrollbar (outer panel desktop only kept for compatibility) */
6396.pa-filters::-webkit-scrollbar { width:0; } /* disembunyikan karena inner yang scroll */
6397
6398/* Utilities */
6399.hidden { display:none !important; }
6400.gradient-text {
6401  background:linear-gradient(90deg,#C9A227,#A16207,#C9A227);
6402  -webkit-background-clip:text;
6403  color:transparent;
6404  animation:titleSheen 10s linear infinite;
6405}
6406.glow-focus:focus {
6407  outline:none !important;
6408  box-shadow:0 0 0 3px rgba(201,162,39,.4) !important;
6409  border-color:#C9A227 !important;
6410}
6411
6412/* Simple fade (opsional) */
6413@keyframes fadeIn {
6414  from { opacity:0; transform:translateY(8px); }
6415  to { opacity:1; transform:translateY(0); }
6416}
6417</style>
6418  <style data-inlined="mega-menu-tiles.css">
6419/* Tile Mega Menu — Modern E-commerce Enhanced (2025) */
6420
6421/* Design Tokens */
6422:root {
6423  --mm2-bg: #ffffff;
6424  --mm2-surface: #ffffff;
6425  --mm2-surface-elevated: rgba(255,255,255,0.98);
6426  --mm2-muted: #f8fafc;
6427  --mm2-border: #e2e8f0;
6428  --mm2-border-strong: #cbd5e1;
6429  --mm2-accent: #B45309;
6430  --mm2-accent-hover: #78350F;
6431  --mm2-accent-10: rgba(99,102,241,.10);
6432  --mm2-accent-20: rgba(99,102,241,.20);
6433  --mm2-fg: #0f172a;
6434  --mm2-fg-muted: #64748b;
6435  --mm2-fg-light: #94a3b8;
6436  
6437  /* Enhanced Shadows - Multi-layer elevation */
6438  --mm2-shadow-sm: 0 2px 8px rgba(15,23,42,.06), 0 1px 2px rgba(15,23,42,.04);
6439  --mm2-shadow-md: 0 8px 24px rgba(15,23,42,.10), 0 2px 8px rgba(15,23,42,.06);
6440  --mm2-shadow-lg: 0 20px 60px rgba(15,23,42,.14), 0 8px 24px rgba(15,23,42,.08);
6441  --mm2-shadow-xl: 0 32px 80px rgba(15,23,42,.18), 0 12px 32px rgba(15,23,42,.10);
6442  --mm2-shadow-hover: 0 16px 40px rgba(15,23,42,.16), 0 6px 16px rgba(15,23,42,.08);
6443  
6444  /* Border Radius */
6445  --mm2-radius-xl: 20px;
6446  --mm2-radius-lg: 16px;
6447  --mm2-radius-md: 12px;
6448  --mm2-radius-sm: 10px;
6449  
6450  --mm2-z: 60;
6451  
6452  /* Animation Timing */
6453  --mm2-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
6454  --mm2-transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1);
6455  --mm2-transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1);
6456}
6457
6458.navbar { position: relative; z-index: var(--mm2-z); }
6459
6460/* Trigger Button - Enhanced */
6461.mm2-trigger {
6462  display: inline-flex; align-items: center; gap: 8px;
6463  padding: 10px 14px; border-radius: var(--mm2-radius-md);
6464  border: 1.5px solid var(--mm2-border);
6465  background: var(--mm2-surface); color: var(--mm2-fg);
6466  cursor: pointer; user-select: none;
6467  transition: all var(--mm2-transition-fast);
6468  text-decoration: none;
6469  font-weight: 600;
6470  box-shadow: var(--mm2-shadow-sm);
6471}
6472.mm2-trigger:hover { 
6473  border-color: var(--mm2-accent); 
6474  background: var(--mm2-muted);
6475  box-shadow: var(--mm2-shadow-md);
6476  transform: translateY(-1px);
6477}
6478.mm2-trigger:active { transform: translateY(0); }
6479.mm2-trigger svg {max-width: unset !important; transition: transform var(--mm2-transition-fast); }
6480.mm2-trigger:hover svg { transform: scale(1.05); }
6481
6482/* Backdrop - Enhanced Glassmorphism */
6483.mm2-backdrop {
6484  position: fixed; 
6485  inset: 0;
6486  top: 0;
6487  left: 0;
6488  right: 0;
6489  bottom: 0;
6490  background: rgba(15,23,42,.40);
6491  backdrop-filter: blur(8px) saturate(1.2);
6492  -webkit-backdrop-filter: blur(8px) saturate(1.2);
6493  opacity: 0; 
6494  pointer-events: none;
6495  z-index: 499;
6496  transition: opacity var(--mm2-transition-base);
6497}
6498.mm2-open .mm2-backdrop { opacity: 1; pointer-events: auto; }
6499
6500/* Panel - Glassmorphism Enhanced */
6501.mm2-panel {
6502  position: fixed;
6503  left: 50%;
6504  top: 120px;
6505  transform: translateX(-50%) translateY(-16px);
6506  width: min(1200px, 95vw);
6507  background: var(--mm2-surface-elevated);
6508  backdrop-filter: blur(20px) saturate(1.8);
6509  -webkit-backdrop-filter: blur(20px) saturate(1.8);
6510  border: 1.5px solid var(--mm2-border-strong);
6511  border-radius: var(--mm2-radius-xl);
6512  box-shadow: var(--mm2-shadow-xl);
6513  display: none;
6514  overflow: hidden;
6515  opacity: 0;
6516  z-index: 500;
6517  transition: transform var(--mm2-transition-slow), opacity var(--mm2-transition-base);
6518}
6519.mm2-open .mm2-panel { 
6520  display: block; 
6521  opacity: 1; 
6522  transform: translateX(-50%) translateY(0);
6523}
6524
6525/* View switcher */
6526.mm2-view { display: none; }
6527.mm2-open .mm2-view.is-active { display: block; }
6528
6529/* Top grid (level-1 tiles) - Enhanced */
6530.mm2-top {
6531  padding: 24px;
6532  max-height: 75vh; overflow: auto;
6533}
6534.mm2-grid {
6535  display: grid;
6536  grid-template-columns: repeat(4, minmax(0,1fr));
6537  gap: 14px;
6538}
6539@media (max-width: 1200px) { .mm2-grid { grid-template-columns: repeat(3, minmax(0,1fr)); } }
6540@media (max-width: 960px)  { .mm2-grid { grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px; } }
6541
6542/* Stagger animation for cards */
6543.mm2-open .mm2-card {
6544  animation: slideInUp 0.3s cubic-bezier(0.4, 0, 0.2, 1) backwards;
6545}
6546.mm2-open .mm2-card:nth-child(1)  { animation-delay: 0.02s; }
6547.mm2-open .mm2-card:nth-child(2)  { animation-delay: 0.04s; }
6548.mm2-open .mm2-card:nth-child(3)  { animation-delay: 0.06s; }
6549.mm2-open .mm2-card:nth-child(4)  { animation-delay: 0.08s; }
6550.mm2-open .mm2-card:nth-child(5)  { animation-delay: 0.10s; }
6551.mm2-open .mm2-card:nth-child(6)  { animation-delay: 0.12s; }
6552.mm2-open .mm2-card:nth-child(7)  { animation-delay: 0.14s; }
6553.mm2-open .mm2-card:nth-child(8)  { animation-delay: 0.16s; }
6554.mm2-open .mm2-card:nth-child(9)  { animation-delay: 0.18s; }
6555.mm2-open .mm2-card:nth-child(10) { animation-delay: 0.20s; }
6556.mm2-open .mm2-card:nth-child(11) { animation-delay: 0.22s; }
6557.mm2-open .mm2-card:nth-child(12) { animation-delay: 0.24s; }
6558.mm2-open .mm2-card:nth-child(n+13) { animation-delay: 0.26s; }
6559
6560@keyframes slideInUp {
6561  from {
6562    opacity: 0;
6563    transform: translateY(12px);
6564  }
6565  to {
6566    opacity: 1;
6567    transform: translateY(0);
6568  }
6569}
6570
6571.mm2-card {
6572  position: relative;
6573  display: flex; gap: 14px; align-items: center;
6574  padding: 16px; border-radius: var(--mm2-radius-md);
6575  background: var(--mm2-surface);
6576  border: 1.5px solid var(--mm2-border);
6577  text-decoration: none; color: var(--mm2-fg);
6578  transition: all var(--mm2-transition-fast);
6579  min-height: 72px;
6580  box-shadow: var(--mm2-shadow-sm);
6581}
6582.mm2-card:hover { 
6583  border-color: var(--mm2-accent);
6584  background: linear-gradient(135deg, rgba(255,255,255,1), rgba(248,250,252,1));
6585  box-shadow: var(--mm2-shadow-hover);
6586  transform: translateY(-3px);
6587}
6588.mm2-card:active { transform: translateY(-1px); }
6589
6590/* Icon with category-specific gradients */
6591.mm2-card .mm2-ico {
6592  width: 48px; height: 48px; border-radius: 12px;
6593  display: inline-flex; align-items: center; justify-content: center;
6594  background: linear-gradient(135deg, #e0e7ff, #fff);
6595  border: 1.5px solid #c7d2fe;
6596  color: #78350F;
6597  flex-shrink: 0;
6598  transition: all var(--mm2-transition-fast);
6599  box-shadow: 0 2px 8px rgba(99,102,241,0.1);
6600}
6601.mm2-card:hover .mm2-ico {
6602  transform: scale(1.08) rotate(2deg);
6603  box-shadow: 0 4px 16px rgba(99,102,241,0.2);
6604}
6605.mm2-card .mm2-ico svg { transition: transform var(--mm2-transition-fast); }
6606.mm2-card:hover .mm2-ico svg { transform: scale(1.1); }
6607
6608.mm2-card .mm2-title { 
6609  font-weight: 700; 
6610  font-size: 15px;
6611  letter-spacing: -0.01em;
6612  line-height: 1.3;
6613}
6614.mm2-card .mm2-sub { 
6615  color: var(--mm2-fg-muted); 
6616  font-size: 12.5px; 
6617  margin-top: 3px;
6618  display: flex;
6619  align-items: center;
6620  gap: 4px;
6621  white-space: nowrap;
6622  overflow: hidden;
6623  text-overflow: ellipsis;
6624}
6625.mm2-card .mm2-count {
6626  display: inline-flex;
6627  padding: 2px 8px;
6628  border-radius: 6px;
6629  background: var(--mm2-accent-10);
6630  color: var(--mm2-accent);
6631  font-weight: 600;
6632  font-size: 11px;
6633  margin-left: 4px;
6634}
6635
6636/* Detail view (level-2 + level-3) - Enhanced */
6637.mm2-detail {
6638  position: relative;
6639  padding: 16px 24px 24px;
6640  max-height: 70vh; overflow: auto;
6641}
6642.mm2-detail-header {
6643  display: flex; align-items: center; gap: 14px;
6644  padding: 10px 0 16px; border-bottom: 2px solid var(--mm2-muted); margin-bottom: 16px;
6645}
6646.mm2-back {
6647  display: inline-flex; align-items: center; justify-content: center;
6648  width: 40px; height: 40px; border-radius: 12px;
6649  border: 1.5px solid var(--mm2-border); background: #fff; color: var(--mm2-fg);
6650  cursor: pointer;
6651  transition: all var(--mm2-transition-fast);
6652  box-shadow: var(--mm2-shadow-sm);
6653}
6654.mm2-back:hover {
6655  border-color: var(--mm2-accent);
6656  background: var(--mm2-muted);
6657  transform: translateX(-3px);
6658  box-shadow: var(--mm2-shadow-md);
6659}
6660.mm2-detail-title { 
6661  font-weight: 800; 
6662  font-size: 18px;
6663  letter-spacing: -0.02em;
6664}
6665
6666/* Two-pane layout (desktop) - Enhanced */
6667.mm2-detail-layout {
6668  display: grid;
6669  grid-template-columns: 280px 1fr;
6670  gap: 20px;
6671}
6672
6673/* Left: subnav (level-2 list) - Enhanced */
6674.mm2-subnav {
6675  position: sticky; top: 8px;
6676  align-self: start;
6677  max-height: calc(70vh - 56px);
6678  overflow: auto;
6679  padding-right: 6px;
6680}
6681.mm2-subnav::-webkit-scrollbar { width: 8px; height: 8px; }
6682.mm2-subnav::-webkit-scrollbar-track { background: transparent; }
6683.mm2-subnav::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 8px; }
6684.mm2-subnav::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
6685
6686.mm2-subitem {
6687  display: flex; align-items: center; justify-content: flex-start; gap: 10px;
6688  width: 100%;
6689  padding: 12px 14px;
6690  border-radius: 12px;
6691  border: 1.5px solid transparent;
6692  background: transparent;
6693  color: var(--mm2-fg);
6694  cursor: pointer;
6695  text-align: left;
6696  transition: all var(--mm2-transition-fast);
6697  margin-bottom: 6px;
6698  font-weight: 600;
6699  font-size: 14px;
6700  position: relative;
6701}
6702.mm2-subitem::before {
6703  content: '';
6704  position: absolute;
6705  left: 0;
6706  top: 50%;
6707  transform: translateY(-50%);
6708  width: 3px;
6709  height: 0;
6710  background: var(--mm2-accent);
6711  border-radius: 0 2px 2px 0;
6712  transition: height var(--mm2-transition-fast);
6713}
6714.mm2-subitem:hover { 
6715  background: var(--mm2-muted);
6716  color: var(--mm2-accent-hover);
6717}
6718.mm2-subitem.active {
6719  background: linear-gradient(135deg, rgba(99,102,241,0.08), rgba(99,102,241,0.04));
6720  border-color: var(--mm2-accent-20);
6721  color: var(--mm2-accent-hover);
6722  box-shadow: inset 0 0 0 1px rgba(99,102,241,0.1);
6723}
6724.mm2-subitem.active::before {
6725  height: 24px;
6726}
6727
6728/* Right: subcontent (level-3 grid) - Enhanced */
6729.mm2-subcontent { min-height: 240px; }
6730.mm2-links {
6731  display: grid; gap: 12px 14px;
6732  grid-template-columns: repeat(3, minmax(0,1fr));
6733}
6734@media (max-width: 1200px) { .mm2-links { grid-template-columns: repeat(2, minmax(0,1fr)); } }
6735
6736.mm2-link {
6737  display: flex; align-items: center; gap: 12px;
6738  padding: 14px; border: 1.5px solid var(--mm2-border); border-radius: var(--mm2-radius-md);
6739  background: var(--mm2-surface); color: var(--mm2-fg); text-decoration: none;
6740  min-height: 52px;
6741  font-weight: 500;
6742  font-size: 14px;
6743  transition: all var(--mm2-transition-fast);
6744  box-shadow: var(--mm2-shadow-sm);
6745  position: relative;
6746  justify-content: space-between;
6747}
6748.mm2-link:hover { 
6749  background: var(--mm2-muted); 
6750  border-color: var(--mm2-accent);
6751  transform: translateX(4px);
6752  box-shadow: var(--mm2-shadow-md);
6753}
6754.mm2-link-text {
6755  flex: 1;
6756}
6757
6758/* Product Thumbnails */
6759.mm2-thumbs {
6760  display: flex;
6761  gap: 6px;
6762  align-items: center;
6763  margin-left: auto;
6764  flex-shrink: 0;
6765}
6766.mm2-thumb {
6767  width: 36px;
6768  height: 36px;
6769  border-radius: 8px;
6770  object-fit: cover;
6771  border: 1.5px solid var(--mm2-border);
6772  box-shadow: 0 2px 6px rgba(0,0,0,0.08);
6773  transition: all var(--mm2-transition-fast);
6774  background: var(--mm2-muted);
6775}
6776.mm2-link:hover .mm2-thumb {
6777  border-color: var(--mm2-accent);
6778  box-shadow: 0 4px 10px rgba(99,102,241,0.15);
6779  transform: scale(1.08);
6780}
6781
6782/* Thumbnail Skeleton Loader */
6783.mm2-thumb-skeleton {
6784  width: 36px;
6785  height: 36px;
6786  border-radius: 8px;
6787  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
6788  background-size: 200% 100%;
6789  animation: shimmer 1.5s infinite;
6790  border: 1.5px solid var(--mm2-border);
6791}
6792
6793.mm2-badge {
6794  font-size: 11px; padding: 3px 10px; margin-left: auto;
6795  border-radius: 12px; background: var(--mm2-accent-10); color: var(--mm2-accent);
6796  font-weight: 700;
6797  border: 1px solid var(--mm2-accent-20);
6798}
6799
6800/* "See all" highlight card - Enhanced */
6801.mm2-seeall {
6802  border: 2px dashed var(--mm2-accent);
6803  background: linear-gradient(135deg, rgba(99,102,241,.08), rgba(99,102,241,.02));
6804  font-weight: 700;
6805}
6806.mm2-seeall:hover {
6807  background: linear-gradient(135deg, rgba(99,102,241,.14), rgba(99,102,241,.06));
6808  border-style: solid;
6809  transform: translateX(6px) scale(1.02);
6810}
6811
6812/* Mobile panel styles - Enhanced with drag handle & better touch targets */
6813@media (max-width: 960px) {
6814  .mm2-panel {
6815    position: fixed;
6816    left: 0;
6817    right: 0;
6818    bottom: 0;
6819    top: auto;
6820    width: 100%;
6821    max-height: 88vh;
6822    transform: translateX(0) translateY(100%);
6823    border-radius: var(--mm2-radius-xl) var(--mm2-radius-xl) 0 0;
6824    border-bottom: none;
6825  }
6826  .mm2-open .mm2-panel {
6827    transform: translateX(0) translateY(0); z-index: 9999;
6828  }
6829  
6830  /* Enhanced backdrop for mobile */
6831  .mm2-backdrop {
6832    backdrop-filter: blur(6px) saturate(1.2);
6833    -webkit-backdrop-filter: blur(6px) saturate(1.2);
6834  }
6835  
6836  /* Mobile close button - Soft glassmorphism */
6837  .mm2-mobile-close {
6838    position: absolute;
6839    top: 4px;
6840    right: 8px;
6841    width: 28px;
6842    height: 28px;
6843    border-radius: 50%;
6844    border: 1px solid rgba(226, 232, 240, 0.6);
6845    background: rgba(255, 255, 255, 0.85);
6846    backdrop-filter: blur(12px) saturate(1.8);
6847    -webkit-backdrop-filter: blur(12px) saturate(1.8);
6848    color: var(--mm2-fg-muted);
6849    display: inline-flex;
6850    align-items: center;
6851    justify-content: center;
6852    cursor: pointer;
6853    transition: all var(--mm2-transition-fast);
6854    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
6855    z-index: 20;
6856  }
6857  .mm2-mobile-close:hover {
6858    background: rgba(254, 242, 242, 0.95);
6859    border-color: rgba(252, 165, 165, 0.8);
6860    color: #dc2626;
6861    box-shadow: 0 4px 12px rgba(220, 38, 38, 0.15);
6862  }
6863  .mm2-mobile-close:active {
6864    transform: scale(0.92);
6865  }
6866  
6867  /* Drag handle indicator */
6868  .mm2-top::before,
6869  .mm2-detail::before {
6870    content: '';
6871    position: sticky;
6872    top: 0;
6873    left: 50%;
6874    transform: translateX(-50%);
6875    width: 40px;
6876    height: 4px;
6877    background: var(--mm2-border-strong);
6878    border-radius: 999px;
6879    margin: 12px auto 20px;
6880    display: block;
6881    z-index: 10;
6882    transition: background var(--mm2-transition-fast);
6883  }
6884  .mm2-panel:active .mm2-top::before,
6885  .mm2-panel:active .mm2-detail::before {
6886    background: #94a3b8;
6887  }
6888  
6889  .mm2-top {
6890    padding: 10px;  
6891  }
6892  
6893  .mm2-top, .mm2-detail { 
6894    max-height: min(82vh, 720px);
6895    padding-top: 8px;
6896  }
6897
6898  /* Two-pane collapses: subnav becomes GRID (no horizontal scroll) */
6899  .mm2-detail-layout { display: block; }
6900  .mm2-subnav {
6901    position: static; max-height: none; overflow: visible;
6902    display: grid; gap: 10px; padding: 10px 4px 16px; margin-bottom: 16px;
6903    grid-template-columns: repeat(2, minmax(0,1fr));
6904    border-bottom: 2px solid var(--mm2-muted);
6905  }
6906  .mm2-subitem {
6907    margin: 0; width: 100%; text-align: center;
6908    border-radius: 14px;
6909    background: #fff; border: 1.5px solid var(--mm2-border);
6910    white-space: normal; line-height: 1.3; min-height: 48px;
6911    justify-content: center;
6912    padding: 12px 10px;
6913    font-size: 13.5px;
6914    box-shadow: var(--mm2-shadow-sm);
6915  }
6916  .mm2-subitem::before { display: none; }
6917  .mm2-subitem:hover {
6918    transform: scale(1.02);
6919  }
6920  .mm2-subitem.active {
6921    background: linear-gradient(135deg, var(--mm2-accent), var(--mm2-accent-hover));
6922    border-color: var(--mm2-accent-hover);
6923    color: white;
6924    box-shadow: 0 4px 12px rgba(99,102,241,0.3);
6925    font-weight: 700;
6926  }
6927
6928  /* Level-3 grid tetap 2 kolom dengan spacing lebih baik */
6929  .mm2-links { 
6930    grid-template-columns: repeat(2, minmax(0,1fr));
6931    gap: 10px;
6932  }
6933  
6934  /* Mobile: hide thumbnails untuk save space */
6935  .mm2-thumbs {
6936    display: none;
6937  }
6938  
6939  /* Larger touch targets for mobile */
6940  .mm2-card {
6941    min-height: 80px;
6942    padding: 18px 14px;
6943  }
6944  .mm2-card .mm2-ico {
6945    width: 50px;
6946    height: 50px;
6947  }
6948  .mm2-card .mm2-title {
6949    font-size: 14px;
6950  }
6951  .mm2-card .mm2-sub {
6952    font-size: 11.5px;
6953  }
6954  .mm2-link {
6955    min-height: 56px;
6956    padding: 16px;
6957  }
6958  .mm2-back {
6959    width: 44px;
6960    height: 44px;
6961  }
6962  
6963  /* Grid spacing */
6964  .mm2-grid {
6965    gap: 10px;
6966  }
6967}
6968</style>
6969  <style data-inlined="product-compare.css">
6970/* Product Compare Feature Styles */
6971
6972/* Compare Button in Product Detail - Modern Chip Style */
6973.pd-compare-section {
6974  margin: 0.5rem 0;
6975  display: flex;
6976  align-items: center;
6977  gap: 0.5rem;
6978}
6979
6980.pd-compare-btn {
6981  display: inline-flex;
6982  align-items: center;
6983  gap: 0.4rem;
6984  padding: 0.5rem 0.85rem;
6985  border: 1px solid #cbd5e1;
6986  border-radius: 20px;
6987  background: #fff;
6988  color: #64748b;
6989  font-weight: 600;
6990  font-size: 0.8rem;
6991  cursor: pointer;
6992  transition: all 0.2s ease;
6993  position: relative;
6994  white-space: nowrap;
6995}
6996
6997.pd-compare-btn:hover {
6998  border-color: #EAB308;
6999  color: #EAB308;
7000  background: #FFFBEB;
7001  transform: translateY(-1px);
7002  box-shadow: 0 2px 6px rgba(234, 179, 8, 0.15);
7003}
7004
7005.pd-compare-btn.added {
7006  border-color: #16a34a;
7007  color: #16a34a;
7008  background: #f0fdf4;
7009}
7010
7011.pd-compare-btn .compare-remove {
7012  display: none;
7013}
7014
7015.pd-compare-btn.added .compare-text {
7016  display: none;
7017}
7018
7019.pd-compare-btn.added .compare-remove {
7020  display: flex !important;
7021  align-items: center;
7022  gap: 0.25rem;
7023  font-weight: 600;
7024  font-size: 0.9rem;
7025  color: #dc2626 !important;
7026  opacity: 1 !important;
7027  visibility: visible !important;
7028  z-index: 1;
7029  position: relative;
7030}
7031
7032.pd-compare-btn.added .compare-remove[hidden] {
7033  display: flex !important;
7034}
7035
7036/* Force visibility for added state */
7037.pd-compare-btn.added .compare-remove {
7038  display: flex !important;
7039}
7040
7041[hidden].compare-remove {
7042  display: none !important;
7043}
7044
7045.pd-compare-btn.added [hidden].compare-remove {
7046  display: flex !important;
7047}
7048
7049/* Ensure the added state text is always visible */
7050.pd-compare-btn.added {
7051  border-color: #dc2626 !important;
7052  color: #dc2626 !important;
7053  background: #fef2f2 !important;
7054}
7055
7056.pd-compare-btn.added .compare-remove svg {
7057  color: #dc2626 !important;
7058}
7059
7060.pd-compare-btn.added:hover {
7061  border-color: #15803d;
7062  color: #15803d;
7063  background: #dcfce7;
7064  transform: translateY(-1px);
7065  box-shadow: 0 2px 6px rgba(22, 163, 74, 0.2);
7066}
7067
7068.pd-compare-btn .compare-icon {
7069  width: 16px;
7070  height: 16px;
7071  flex-shrink: 0;
7072}
7073
7074.pd-compare-info {
7075  display: inline-flex;
7076  align-items: center;
7077}
7078
7079.compare-note {
7080  color: #94a3b8;
7081  font-size: 0.7rem;
7082  font-weight: 500;
7083  transition: color 0.2s ease;
7084}
7085
7086.pd-compare-btn.added + .pd-compare-info .compare-note {
7087  color: #16a34a;
7088  font-weight: 600;
7089}
7090
7091/* Compare Badge in Navigation */
7092.compare-indicator {
7093  position: relative;
7094}
7095
7096.compare-indicator .badge {
7097  position: absolute;
7098  top: -8px;
7099  right: -8px;
7100  min-width: 18px;
7101  height: 18px;
7102  padding: 0 5px;
7103  background: #ef4444;
7104  color: white;
7105  border-radius: 9px;
7106  font-size: 11px;
7107 
7108
7109/* Floating Compare Badge (Sticky) */
7110.compare-floating-badge {
7111  position: fixed;
7112  right: 16px;
7113  bottom: calc(20px + 60px); /* Above scroll-to-top button */
7114  width: auto;
7115  min-width: 56px;
7116  height: 56px;
7117  padding: 0 16px;
7118  border-radius: 28px;
7119  background: linear-gradient(135deg, #EAB308 0%, #CA8A04 100%);
7120  color: #fff;
7121  box-shadow: 0 8px 24px -8px rgba(234, 179, 8, 0.5);
7122  cursor: pointer;
7123  z-index: 1104;
7124  opacity: 0;
7125  transform: translateY(8px) scale(0.9);
7126  transition: opacity 0.3s ease, transform 0.3s ease, box-shadow 0.2s ease;
7127  display: flex;
7128  align-items: center;
7129  justify-content: center;
7130  gap: 8px;
7131  font-weight: 600;
7132  font-size: 0.85rem;
7133  pointer-events: none;
7134}
7135
7136.compare-floating-badge.visible {
7137  opacity: 1;
7138  transform: translateY(0) scale(1);
7139  pointer-events: auto;
7140}
7141
7142.compare-floating-badge:hover {
7143  box-shadow: 0 12px 32px -10px rgba(234, 179, 8, 0.6);
7144  transform: translateY(-2px) scale(1.02);
7145}
7146
7147.compare-floating-badge:active {
7148  transform: translateY(0) scale(0.98);
7149}
7150
7151.compare-floating-badge .cfb-icon {
7152  width: 20px;
7153  height: 20px;
7154  flex-shrink: 0;
7155}
7156
7157.compare-floating-badge .cfb-count {
7158  font-size: 0.9rem;
7159  font-weight: 700;
7160  min-width: 20px;
7161  text-align: center;
7162  background: rgba(255, 255, 255, 0.25);
7163  padding: 2px 8px;
7164  border-radius: 12px;
7165  line-height: 1;
7166  display: flex;
7167  align-items: center;
7168  justify-content: center;
7169  height: 24px;
7170}
7171
7172.compare-floating-badge .cfb-text {
7173  display: none;
7174}
7175
7176@media (min-width: 641px) {
7177  .compare-floating-badge .cfb-text {
7178    display: block;
7179    white-space: nowrap;
7180  }
7181}
7182
7183@media (max-width: 640px) {
7184  .compare-floating-badge {
7185    right: 14px;
7186    bottom: calc(var(--scroll-top-bottom, calc(68px + 18px + env(safe-area-inset-bottom))) + 60px);
7187    min-width: 48px;
7188    height: 48px;
7189    padding: 0 12px;
7190    border-radius: 24px;
7191  }
7192  
7193  .compare-floating-badge .cfb-icon {
7194    width: 18px;
7195    height: 18px;
7196  }
7197  
7198  .compare-floating-badge .cfb-count {
7199    font-size: 0.8rem;
7200    padding: 2px 6px;
7201    height: 20px;
7202  }
7203} font-weight: 600;
7204  display: flex;
7205  align-items: center;
7206  justify-content: center;
7207  transform: scale(0);
7208  transition: transform 0.2s ease;
7209}
7210
7211.compare-indicator .badge.active {
7212  transform: scale(1);
7213}
7214
7215/* Compare Sidebar */
7216.compare-sidebar {
7217  position: fixed;
7218  top: 0;
7219  right: -400px;
7220  width: 400px;
7221  height: 100vh;
7222  background: #ffffff;
7223  box-shadow: -4px 0 20px rgba(0, 0, 0, 0.1);
7224  z-index: 9999;
7225  display: flex;
7226  flex-direction: column;
7227  transition: right 0.3s ease;
7228}
7229
7230.compare-sidebar.open {
7231  right: 0;
7232}
7233
7234.compare-overlay {
7235  position: fixed;
7236  top: 0;
7237  left: 0;
7238  width: 100%;
7239  height: 100%;
7240  background: rgba(0, 0, 0, 0.5);
7241  z-index: 9998;
7242  opacity: 0;
7243  visibility: hidden;
7244  transition: all 0.3s ease;
7245}
7246
7247.compare-overlay.active {
7248  opacity: 1;
7249  visibility: visible;
7250}
7251
7252.compare-header {
7253  display: flex;
7254  align-items: center;
7255  justify-content: space-between;
7256  padding: 1.25rem;
7257  border-bottom: 1px solid #e2e8f0;
7258  background: #f8fafc;
7259}
7260
7261.compare-title {
7262  display: flex;
7263  align-items: center;
7264  gap: 0.5rem;
7265  font-size: 1rem;
7266  font-weight: 600;
7267  color: #1e293b;
7268  margin: 0;
7269}
7270
7271.compare-count {
7272  color: #64748b;
7273  font-weight: 400;
7274}
7275
7276.compare-close {
7277  display: flex;
7278  align-items: center;
7279  justify-content: center;
7280  width: 32px;
7281  height: 32px;
7282  border: none;
7283  border-radius: 6px;
7284  background: transparent;
7285  color: #64748b;
7286  cursor: pointer;
7287  transition: all 0.2s ease;
7288}
7289
7290.compare-close:hover {
7291  background: #e2e8f0;
7292  color: #374151;
7293}
7294
7295.compare-content {
7296  flex: 1;
7297  overflow-y: auto;
7298  padding: 1rem;
7299}
7300
7301.compare-empty {
7302  display: flex;
7303  flex-direction: column;
7304  align-items: center;
7305  justify-content: center;
7306  height: 100%;
7307  text-align: center;
7308  padding: 2rem 1rem;
7309}
7310
7311.compare-empty h5 {
7312  margin: 1rem 0 0.5rem;
7313  color: #374151;
7314}
7315
7316.compare-empty p {
7317  color: #64748b;
7318  font-size: 0.875rem;
7319  line-height: 1.5;
7320}
7321
7322.compare-list {
7323  display: flex;
7324  flex-direction: column;
7325  gap: 0.75rem;
7326}
7327
7328.compare-item {
7329  display: flex;
7330  align-items: center;
7331  gap: 0.75rem;
7332  padding: 0.75rem;
7333  border: 1px solid #e2e8f0;
7334  border-radius: 8px;
7335  background: #ffffff;
7336  transition: all 0.2s ease;
7337}
7338
7339.compare-item:hover {
7340  border-color: #cbd5e1;
7341  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
7342}
7343
7344.compare-item-image {
7345  flex-shrink: 0;
7346  width: 48px;
7347  height: 48px;
7348  border-radius: 6px;
7349  overflow: hidden;
7350  border: 1px solid #e2e8f0;
7351}
7352
7353.compare-item-image img {
7354  width: 100%;
7355  height: 100%;
7356  object-fit: cover;
7357}
7358
7359.compare-item-info {
7360  flex: 1;
7361  min-width: 0;
7362}
7363
7364.compare-item-name {
7365  font-weight: 500;
7366  font-size: 0.875rem;
7367  color: #1e293b;
7368  margin-bottom: 0.25rem;
7369  line-height: 1.3;
7370  display: -webkit-box;
7371  -webkit-line-clamp: 2;
7372  -webkit-box-orient: vertical;
7373  overflow: hidden;
7374}
7375
7376.compare-item-price {
7377  font-weight: 600;
7378  font-size: 0.8rem;
7379  color: #059669;
7380}
7381
7382.compare-item-remove {
7383  flex-shrink: 0;
7384  display: flex;
7385  align-items: center;
7386  justify-content: center;
7387  width: 28px;
7388  height: 28px;
7389  border: none;
7390  border-radius: 4px;
7391  background: transparent;
7392  color: #dc2626;
7393  cursor: pointer;
7394  transition: all 0.2s ease;
7395}
7396
7397.compare-item-remove:hover {
7398  background: #fef2f2;
7399  color: #991b1b;
7400}
7401
7402.compare-actions {
7403  padding: 1rem;
7404  border-top: 1px solid #e2e8f0;
7405  display: flex;
7406  gap: 0.75rem;
7407}
7408
7409.compare-actions .btn {
7410  flex: 1;
7411  padding: 0.75rem 1rem;
7412  border-radius: 6px;
7413  font-weight: 600;
7414  font-size: 0.875rem;
7415  display: flex;
7416  align-items: center;
7417  justify-content: center;
7418  gap: 0.5rem;
7419  cursor: pointer;
7420  transition: all 0.2s ease;
7421}
7422
7423.btn-outline-danger {
7424  border: 1px solid #dc2626;
7425  background: transparent;
7426  color: #dc2626;
7427}
7428
7429.btn-outline-danger:hover {
7430  background: #dc2626;
7431  color: white;
7432}
7433
7434.btn-primary {
7435  border: 1px solid #EAB308;
7436  background: #EAB308;
7437  color: white;
7438}
7439
7440.btn-primary:hover {
7441  background: #CA8A04;
7442  border-color: #CA8A04;
7443}
7444
7445.btn-primary:disabled {
7446  opacity: 0.5;
7447  cursor: not-allowed;
7448}
7449
7450.btn-primary:disabled:hover {
7451  background: #EAB308;
7452  border-color: #EAB308;
7453}
7454
7455/* AI Comparison Modal */
7456.ai-compare-modal {
7457  position: fixed;
7458  top: 0;
7459  left: 0;
7460  width: 100%;
7461  height: 100%;
7462  z-index: 10000;
7463  display: none;
7464  align-items: center;
7465  justify-content: center;
7466}
7467
7468.ai-compare-modal.open {
7469  display: flex;
7470}
7471
7472.ai-compare-modal .modal-backdrop {
7473  position: absolute;
7474  top: 0;
7475  left: 0;
7476  width: 100%;
7477  height: 100%;
7478  background: rgba(0, 0, 0, 0.6);
7479  backdrop-filter: blur(4px);
7480}
7481
7482.modal-container {
7483  position: relative;
7484  width: 90%;
7485  max-width: 1000px;
7486  max-height: 90vh;
7487  background: #ffffff;
7488  border-radius: 12px;
7489  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
7490  display: flex;
7491  flex-direction: column;
7492  overflow: hidden;
7493}
7494
7495.modal-header {
7496  display: flex;
7497  align-items: center;
7498  justify-content: space-between;
7499  padding: 1.5rem 2rem;
7500  border-bottom: 1px solid #e2e8f0;
7501  background: #f8fafc;
7502}
7503
7504.modal-title {
7505  display: flex;
7506  align-items: center;
7507  gap: 0.75rem;
7508  font-size: 1.25rem;
7509  font-weight: 600;
7510  color: #1e293b;
7511  margin: 0;
7512}
7513
7514.modal-close {
7515  display: flex;
7516  align-items: center;
7517  justify-content: center;
7518  width: 36px;
7519  height: 36px;
7520  border: none;
7521  border-radius: 8px;
7522  background: transparent;
7523  color: #64748b;
7524  cursor: pointer;
7525  transition: all 0.2s ease;
7526}
7527
7528.modal-close:hover {
7529  background: #e2e8f0;
7530  color: #374151;
7531}
7532
7533.modal-content {
7534  flex: 1;
7535  overflow-y: auto;
7536  padding: 2rem;
7537}
7538
7539/* AI Loading State */
7540.ai-loading {
7541  display: flex;
7542  flex-direction: column;
7543  align-items: center;
7544  justify-content: center;
7545  padding: 3rem 2rem;
7546  text-align: center;
7547}
7548
7549.loading-animation {
7550  margin-bottom: 1rem;
7551}
7552
7553.typing-indicator {
7554  display: flex;
7555  align-items: center;
7556  justify-content: center;
7557  gap: 4px;
7558  margin-bottom: 1rem;
7559}
7560
7561.typing-indicator span {
7562  width: 8px;
7563  height: 8px;
7564  background: #EAB308;
7565  border-radius: 50%;
7566  animation: typing 1.4s infinite ease-in-out;
7567}
7568
7569.typing-indicator span:nth-child(2) {
7570  animation-delay: 0.2s;
7571}
7572
7573.typing-indicator span:nth-child(3) {
7574  animation-delay: 0.4s;
7575}
7576
7577@keyframes typing {
7578  0%, 80%, 100% {
7579    opacity: 0.3;
7580    transform: scale(0.8);
7581  }
7582  40% {
7583    opacity: 1;
7584    transform: scale(1);
7585  }
7586}
7587
7588.ai-loading p {
7589  font-weight: 500;
7590  color: #1e293b;
7591  margin-bottom: 0.5rem;
7592}
7593
7594.ai-loading small {
7595  color: #64748b;
7596}
7597
7598/* AI Error State */
7599.ai-error {
7600  display: flex;
7601  flex-direction: column;
7602  align-items: center;
7603  justify-content: center;
7604  padding: 3rem 2rem;
7605  text-align: center;
7606}
7607
7608.ai-error h4 {
7609  margin: 1rem 0 0.5rem;
7610  color: #dc2626;
7611}
7612
7613.ai-error p {
7614  color: #64748b;
7615  margin-bottom: 1.5rem;
7616}
7617
7618/* AI Results */
7619.ai-results > div {
7620  margin-bottom: 2rem;
7621}
7622
7623.ai-summary-card,
7624.ai-comparison-table,
7625.ai-detailed-analysis,
7626.ai-recommendations,
7627.ai-tips {
7628  background: #f8fafc;
7629  border: 1px solid #e2e8f0;
7630  border-radius: 12px;
7631  padding: 1.5rem;
7632}
7633
7634.ai-results h4 {
7635  display: flex;
7636  align-items: center;
7637  gap: 0.5rem;
7638  font-size: 1.1rem;
7639  font-weight: 600;
7640  color: #1e293b;
7641  margin-bottom: 1rem;
7642}
7643
7644.summary-text {
7645  font-size: 1rem;
7646  line-height: 1.6;
7647  color: #374151;
7648  margin-bottom: 1rem;
7649}
7650
7651.winner-badge {
7652  padding: 1rem;
7653  background: linear-gradient(135deg, #10b981 0%, #059669 100%);
7654  color: white;
7655  border-radius: 8px;
7656}
7657
7658.winner-badge strong {
7659  display: block;
7660  margin-bottom: 0.25rem;
7661}
7662
7663.winner-badge small {
7664  opacity: 0.9;
7665  font-size: 0.875rem;
7666}
7667
7668/* Comparison Table */
7669.comparison-grid {
7670  display: grid;
7671  gap: 1rem;
7672  margin-top: 1rem;
7673}
7674
7675.comparison-category {
7676  background: white;
7677  border: 1px solid #e2e8f0;
7678  border-radius: 8px;
7679  overflow: hidden;
7680}
7681
7682.category-header {
7683  background: #f1f5f9;
7684  padding: 0.75rem 1rem;
7685  font-weight: 600;
7686  color: #1e293b;
7687  border-bottom: 1px solid #e2e8f0;
7688}
7689
7690.category-products {
7691  display: grid;
7692  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
7693  gap: 1px;
7694  background: #e2e8f0;
7695}
7696
7697.product-score {
7698  background: white;
7699  padding: 1rem;
7700  text-align: center;
7701}
7702
7703.score-value {
7704  display: inline-flex;
7705  align-items: center;
7706  gap: 0.25rem;
7707  margin-bottom: 0.5rem;
7708}
7709
7710.score-stars {
7711  color: #EAB308;
7712}
7713
7714.score-note {
7715  font-size: 0.8rem;
7716  color: #64748b;
7717  line-height: 1.3;
7718}
7719
7720/* Product name header in comparison table */
7721.product-name-header {
7722  margin-bottom: 0.5rem;
7723}
7724
7725.product-name-header strong {
7726  font-size: .8rem; /* h5 size (18px) */
7727  font-weight: 600;
7728  color: #1e293b;
7729  line-height: 1.3;
7730  display: block;
7731  word-break: break-word;
7732}
7733
7734.product-price {
7735  font-size: 0.8rem;
7736  font-weight: 600;
7737  color: #059669;
7738}
7739
7740/* Detailed Analysis */
7741.products-analysis {
7742  display: grid;
7743  gap: 1rem;
7744  margin-top: 1rem;
7745}
7746
7747.product-analysis {
7748  background: white;
7749  border: 1px solid #e2e8f0;
7750  border-radius: 8px;
7751  padding: 1rem;
7752}
7753
7754.product-analysis h5 {
7755  color: #1e293b;
7756  margin-bottom: 0.75rem;
7757  font-weight: 600;
7758}
7759
7760.analysis-section {
7761  margin-bottom: 0.75rem;
7762}
7763
7764.analysis-section:last-child {
7765  margin-bottom: 0;
7766}
7767
7768.analysis-section h6 {
7769  font-size: 0.875rem;
7770  font-weight: 600;
7771  margin-bottom: 0.5rem;
7772}
7773
7774.analysis-section h6.strengths {
7775  color: #059669;
7776}
7777
7778.analysis-section h6.weaknesses {
7779  color: #dc2626;
7780}
7781
7782.analysis-section h6.best-for {
7783  color: #8B6914;
7784}
7785
7786.analysis-section ul {
7787  list-style: none;
7788  padding: 0;
7789  margin: 0;
7790}
7791
7792.analysis-section li {
7793  display: flex;
7794  align-items: flex-start;
7795  gap: 0.5rem;
7796  margin-bottom: 0.25rem;
7797  font-size: 0.875rem;
7798  line-height: 1.4;
7799}
7800
7801.analysis-section li:before {
7802  content: "•";
7803  color: #64748b;
7804  font-weight: bold;
7805  margin-top: 0.1rem;
7806}
7807
7808.best-for-text {
7809  font-size: 0.875rem;
7810  color: #374151;
7811  font-style: italic;
7812}
7813
7814/* Recommendations */
7815.recommendations-grid {
7816  display: grid;
7817  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
7818  gap: 1rem;
7819  margin-top: 1rem;
7820}
7821
7822.recommendation-card {
7823  background: white;
7824  border: 1px solid #e2e8f0;
7825  border-radius: 8px;
7826  padding: 1rem;
7827  text-align: center;
7828}
7829
7830.recommendation-card h5 {
7831  font-size: 1rem;
7832  margin-bottom: 0.75rem;
7833  color: #1e293b;
7834}
7835
7836.recommendation-card p {
7837  font-size: 0.875rem;
7838  color: #374151;
7839  line-height: 1.5;
7840}
7841
7842/* Tips */
7843.tips-list {
7844  list-style: none;
7845  padding: 0;
7846  margin: 1rem 0 0 0;
7847}
7848
7849.tips-list li {
7850  display: flex;
7851  align-items: flex-start;
7852  gap: 0.75rem;
7853  margin-bottom: 0.75rem;
7854  font-size: 0.9rem;
7855  line-height: 1.5;
7856  color: #374151;
7857}
7858
7859.tips-list li:before {
7860  content: "💡";
7861  font-size: 1rem;
7862  margin-top: 0.1rem;
7863}
7864
7865/* Modal Footer */
7866.modal-footer {
7867  display: flex;
7868  justify-content: space-between;
7869  align-items: center;
7870  padding: 1.5rem 2rem;
7871  border-top: 1px solid #e2e8f0;
7872  background: #f8fafc;
7873}
7874
7875.btn-outline-secondary {
7876  border: 1px solid #64748b;
7877  background: transparent;
7878  color: #64748b;
7879  padding: 0.75rem 1.5rem;
7880  border-radius: 6px;
7881  font-weight: 600;
7882  display: flex;
7883  align-items: center;
7884  gap: 0.5rem;
7885  cursor: pointer;
7886  transition: all 0.2s ease;
7887}
7888
7889.btn-outline-secondary:hover {
7890  background: #64748b;
7891  color: white;
7892}
7893
7894/* Responsive for chip button */
7895@media (max-width: 880px) {
7896  .pd-compare-section {
7897    margin: 1rem 0;
7898    display: block;
7899  }
7900
7901  .pd-compare-btn {
7902    width: 100%;
7903    font-size: 0.85rem;
7904    padding: 0.65rem 1rem;
7905    border-radius: 10px;
7906    justify-content: center;
7907    min-height: 44px;
7908  }
7909  
7910  .pd-compare-btn .compare-icon {
7911    width: 16px;
7912    height: 16px;
7913  }
7914
7915  .pd-compare-info {
7916    margin-top: 0.5rem;
7917    display: block;
7918    text-align: center;
7919  }
7920
7921  .compare-note {
7922    font-size: 0.7rem;
7923    display: block;
7924  }
7925}
7926
7927/* Responsive Design */
7928@media (max-width: 768px) {
7929  .compare-sidebar {
7930    width: 100%;
7931    right: -100%;
7932    /* Fix for mobile viewport height changes */
7933    height: 100vh;
7934    height: 100dvh; /* Dynamic viewport height for modern browsers */
7935  }
7936  
7937  .compare-actions {
7938    /* Ensure actions are always visible */
7939    position: sticky;
7940    bottom: 0;
7941    background: white;
7942    border-top: 2px solid #e2e8f0;
7943    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
7944    z-index: 10;
7945  }
7946  
7947  .compare-actions .btn {
7948    /* Larger touch targets for mobile */
7949    min-height: 48px;
7950    font-size: 0.9rem;
7951    font-weight: 700;
7952  }
7953  
7954  .modal-container {
7955    width: 95%;
7956    max-height: 95vh;
7957    max-height: 95dvh; /* Dynamic viewport height */
7958  }
7959  
7960  .modal-header,
7961  .modal-content,
7962  .modal-footer {
7963    padding: 1rem;
7964  }
7965  
7966  .recommendations-grid {
7967    grid-template-columns: 1fr;
7968  }
7969  
7970  .category-products {
7971    grid-template-columns: 1fr;
7972  }
7973  
7974  .modal-footer {
7975    flex-direction: column;
7976    gap: 0.75rem;
7977  }
7978  
7979  .modal-footer .btn {
7980    width: 100%;
7981  }
7982  
7983  /* Ensure compare button is visible on mobile */
7984  .pd-compare-section {
7985    display: block !important;
7986    visibility: visible !important;
7987    opacity: 1 !important;
7988    margin: 1rem 0;
7989    padding: 0;
7990    position: relative !important;
7991    z-index: 1 !important;
7992    max-width: 100%;
7993    overflow: hidden;
7994  }
7995  
7996  .pd-compare-btn {
7997    display: flex !important;
7998    visibility: visible !important;
7999    opacity: 1 !important;
8000    width: 100%;
8001    padding: 0.75rem 1rem;
8002    font-size: 0.9rem;
8003    min-height: 48px; /* Minimum touch target for mobile */
8004    border: 2px solid #e2e8f0 !important;
8005    background: #fff !important;
8006    position: relative !important;
8007    z-index: 2 !important;
8008    box-sizing: border-box;
8009    white-space: normal !important; /* Allow text wrapping on mobile */
8010    flex-wrap: wrap;
8011    gap: 0.5rem;
8012  }
8013  
8014  .pd-compare-btn .compare-text {
8015    display: inline !important;
8016    visibility: visible !important;
8017    font-weight: 600;
8018    color: #475569 !important;
8019  }
8020  
8021  .pd-compare-btn .compare-remove {
8022    display: none !important; /* Hidden by default */
8023  }
8024  
8025  .pd-compare-btn.added .compare-remove {
8026    display: flex !important; /* Only show when added */
8027    flex-wrap: wrap;
8028    align-items: center;
8029    gap: 0.4rem;
8030    white-space: normal !important;
8031    word-break: break-word;
8032  }
8033  
8034  .pd-compare-btn .compare-icon {
8035    display: inline-block !important;
8036    width: 18px;
8037    height: 18px;
8038    flex-shrink: 0;
8039  }
8040  
8041  .pd-compare-info {
8042    margin-top: 0.5rem;
8043  }
8044  
8045  .compare-note {
8046    font-size: 0.85rem !important;
8047    display: block !important;
8048    visibility: visible !important;
8049    color: #64748b !important;
8050  }
8051}
8052
8053/* iOS Safari specific fixes */
8054@media (max-width: 480px) {
8055  #compareBtn { 
8056    width:46px;
8057    height:46px;
8058  }
8059  
8060  .pd-compare-section {
8061    -webkit-transform: translateZ(0);
8062    transform: translateZ(0);
8063  }
8064  
8065  .pd-compare-btn {
8066    -webkit-appearance: none !important;
8067    appearance: none !important;
8068    -webkit-tap-highlight-color: transparent;
8069    touch-action: manipulation;
8070  }
8071  
8072  /* Fix sidebar height for iOS address bar */
8073  .compare-sidebar {
8074    /* Use fixed positioning that accounts for iOS address bar */
8075    height: 100vh;
8076    height: -webkit-fill-available; /* iOS Safari compatibility */
8077    min-height: 100vh;
8078    min-height: -webkit-fill-available;
8079  }
8080  
8081  /* Ensure compare actions stay at bottom */
8082  .compare-actions {
8083    position: sticky;
8084    bottom: 0;
8085    background: white;
8086    border-top: 2px solid #e2e8f0;
8087    box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.15);
8088    padding: 1.25rem 1rem;
8089    z-index: 20;
8090  }
8091  
8092  .compare-actions .btn {
8093    min-height: 52px; /* Larger for easier tapping */
8094    font-size: 1rem;
8095    font-weight: 700;
8096    border-radius: 8px;
8097  }
8098  
8099  /* Adjust content padding to account for sticky actions */
8100  .compare-content {
8101    padding-bottom: 6rem; /* Extra space for sticky actions */
8102  }
8103}
8104
8105/* High-resolution mobile devices (iPhone 12 Pro, etc.) */
8106@media (-webkit-min-device-pixel-ratio: 2) and (max-width: 430px) {
8107  .pd-compare-section {
8108    display: block !important;
8109  }
8110  
8111  .pd-compare-btn {
8112    display: flex !important;
8113    min-height: 44px;
8114    font-size: 16px; /* Prevent zoom on iOS */
8115  }
8116  
8117  /* Enhanced fixes for iPhone 12 Pro and similar devices */
8118  .compare-sidebar {
8119    /* Ensure full height coverage */
8120    height: 100vh;
8121    height: 100dvh; /* Dynamic viewport height */
8122    height: -webkit-fill-available; /* iOS fallback */
8123    max-height: 100vh;
8124    max-height: 100dvh;
8125    max-height: -webkit-fill-available;
8126    overflow: hidden; /* Prevent scroll issues */
8127  }
8128  
8129  .compare-content {
8130    /* Flexible content area */
8131    flex: 1;
8132    overflow-y: auto;
8133    -webkit-overflow-scrolling: touch;
8134    padding: 1rem 1rem 8rem 1rem; /* Extra bottom padding */
8135  }
8136  
8137  .compare-actions {
8138    /* Force actions to stay visible */
8139    position: fixed;
8140    bottom: 0;
8141    left: 0;
8142    right: 0;
8143    width: 100%;
8144    background: white;
8145    border-top: 2px solid #e2e8f0;
8146    box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.2);
8147    padding: 1rem;
8148    z-index: 9999;
8149    display: flex !important;
8150    gap: 0.75rem;
8151  }
8152  
8153  .compare-actions .btn {
8154    flex: 1;
8155    min-height: 30px; /* Extra large for high-DPI screens */
8156    font-size: .8rem;
8157    font-weight: 600;
8158    border-radius: 12px;
8159    display: flex !important;
8160    align-items: center;
8161    justify-content: center;
8162    gap: 0.5rem;
8163  }
8164  
8165  /* Ensure AI analyze button is always visible and prominent */
8166  #compareAnalyze {
8167    background: #EAB308 !important;
8168    border-color: #EAB308 !important;
8169    color: white !important;
8170    box-shadow: 0 4px 12px rgba(234, 179, 8, 0.3);
8171  }
8172  
8173  #compareAnalyze:not(:disabled) {
8174    background: #CA8A04 !important;
8175    border-color: #CA8A04 !important;
8176    transform: scale(1.02);
8177  }
8178  
8179  #compareAnalyze:disabled {
8180    opacity: 0.6 !important;
8181    background: #9ca3af !important;
8182    border-color: #9ca3af !important;
8183  }
8184}
8185
8186/* AI Disclaimer Styles */
8187.ai-disclaimer {
8188  display: flex;
8189  align-items: flex-start;
8190  gap: 0.75rem;
8191  padding: 1rem;
8192  background: #FEF3C7;
8193  border: 1px solid #EAB308;
8194  border-radius: 8px;
8195  margin: 0;
8196}
8197
8198.disclaimer-icon {
8199  font-size: 1.25rem;
8200  flex-shrink: 0;
8201  margin-top: 0.125rem;
8202}
8203
8204.disclaimer-text {
8205  font-size: 0.85rem;
8206  line-height: 1.4;
8207  color: #8B6914;
8208}
8209
8210.disclaimer-text strong {
8211  color: #78350F;
8212  font-weight: 600;
8213}
8214
8215/* Compare Disclaimer in Sidebar */
8216.compare-disclaimer {
8217  margin-top: 1rem;
8218  padding: 1rem;
8219  background: #FFFBEB;
8220  border: 1px solid #EAB308;
8221  border-radius: 8px;
8222}
8223
8224.compare-disclaimer h6 {
8225  display: flex;
8226  align-items: center;
8227  gap: 0.5rem;
8228  font-size: 0.875rem;
8229  font-weight: 600;
8230  color: #0c4a6e;
8231  margin-bottom: 0.5rem;
8232}
8233
8234.compare-disclaimer p {
8235  font-size: 0.8rem;
8236  line-height: 1.4;
8237  color: #075985;
8238  margin-bottom: 0.5rem;
8239}
8240
8241.compare-disclaimer p:last-child {
8242  margin-bottom: 0;
8243}
8244
8245.compare-disclaimer ul {
8246  list-style: none;
8247  padding: 0;
8248  margin: 0.5rem 0 0 0;
8249}
8250
8251.compare-disclaimer li {
8252  display: flex;
8253  align-items: flex-start;
8254  gap: 0.5rem;
8255  margin-bottom: 0.25rem;
8256  font-size: 0.75rem;
8257  line-height: 1.3;
8258  color: #075985;
8259}
8260
8261.compare-disclaimer li:before {
8262  content: "•";
8263  color: #EAB308;
8264  font-weight: bold;
8265  margin-top: 0.1rem;
8266  flex-shrink: 0;
8267}
8268
8269/* Mobile Browser Address Bar Fixes */
8270@supports (height: 100dvh) {
8271  /* Modern browsers with dynamic viewport support */
8272  @media (max-width: 768px) {
8273    .compare-sidebar {
8274      height: 100dvh !important;
8275      max-height: 100dvh !important;
8276    }
8277    
8278    .modal-container {
8279      max-height: 95dvh !important;
8280    }
8281  }
8282}
8283
8284@supports (height: -webkit-fill-available) {
8285  /* iOS Safari specific support */
8286  @media (max-width: 768px) {
8287    .compare-sidebar {
8288      height: -webkit-fill-available !important;
8289      max-height: -webkit-fill-available !important;
8290    }
8291  }
8292}
8293
8294/* Fallback for older browsers */
8295@media (max-width: 768px) and (orientation: portrait) {
8296  .compare-sidebar {
8297    /* Fixed height that works across different mobile browsers */
8298    min-height: 600px;
8299    height: calc(100vh - env(keyboard-inset-height, 0px));
8300    height: calc(100vh - env(safe-area-inset-bottom, 0px));
8301  }
8302  
8303  /* Ensure actions are always accessible */
8304  .compare-actions {
8305    position: sticky !important;
8306    bottom: env(safe-area-inset-bottom, 0px) !important;
8307    margin-bottom: 0 !important;
8308    padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0px)) !important;
8309  }
8310}
8311
8312/* PWA and fullscreen app support */
8313@media (display-mode: standalone) {
8314  .compare-sidebar {
8315    height: 100vh !important;
8316    height: 100dvh !important;
8317  }
8318}
8319
8320/* Utilities */
8321.btn-sm {
8322  padding: 0.5rem 0.75rem;
8323  font-size: 0.8rem;
8324}
8325
8326[hidden] {
8327  display: none !important;
8328}
8329
8330/* ===== UX ENHANCEMENTS ===== */
8331
8332/* Compare Button Highlight Animation */
8333.pulse-highlight {
8334  animation: pulse-glow 2s ease-in-out 3;
8335  box-shadow: 0 0 0 0 rgba(234, 179, 8, 0.7);
8336}
8337
8338@keyframes pulse-glow {
8339  0% {
8340    box-shadow: 0 0 0 0 rgba(234, 179, 8, 0.7);
8341  }
8342  50% {
8343    box-shadow: 0 0 0 10px rgba(234, 179, 8, 0);
8344  }
8345  100% {
8346    box-shadow: 0 0 0 0 rgba(234, 179, 8, 0);
8347  }
8348}
8349
8350/* Flying Product Animation */
8351.flying-product {
8352  position: fixed;
8353  z-index: 9999;
8354  pointer-events: none;
8355}
8356
8357.flying-product img {
8358  border-radius: 8px;
8359  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
8360}
8361
8362/* Sticky Compare Bar - REMOVED (using only fly-to-navbar animation) */
8363
8364/* Inline Compare Preview - REMOVED (keeping it simple and clean) */
8365</style>
8366  <style data-inlined="product-card-actions.css">
8367/**
8368 * Product Card Quick Actions - 3 Dot Menu + Bottom Sheet
8369 * Tokopedia-inspired design
8370 */
8371
8372/* ===== 3 Dot Button on Product Card ===== */
8373.prod-card {
8374    position: relative;
8375}
8376
8377.prod-card-actions-btn {
8378    position: absolute;
8379    top: 8px;
8380    right: 8px;
8381    width: 32px;
8382    height: 32px;
8383    border-radius: 50%;
8384    background: rgba(255, 255, 255, 0.95);
8385    border: 1px solid rgba(0, 0, 0, 0.08);
8386    display: inline-flex;
8387    align-items: center;
8388    justify-content: center;
8389    cursor: pointer;
8390    z-index: 3;
8391    transition: all 0.2s ease;
8392    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
8393    -webkit-tap-highlight-color: transparent;
8394}
8395
8396.prod-card-actions-btn:hover {
8397    background: #fff;
8398    border-color: rgba(0, 0, 0, 0.15);
8399    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
8400    transform: scale(1.05);
8401}
8402
8403.prod-card-actions-btn:active {
8404    transform: scale(0.95);
8405}
8406
8407.prod-card-actions-btn svg {
8408    width: 20px;
8409    height: 20px;
8410    color: #64748b;
8411    display: block;
8412}
8413
8414/* Desktop: Show on hover */
8415@media (min-width: 641px) {
8416    .prod-card-actions-btn {
8417        opacity: 0;
8418        pointer-events: none;
8419    }
8420    
8421    .prod-card:hover .prod-card-actions-btn {
8422        opacity: 1;
8423        pointer-events: auto;
8424    }
8425}
8426
8427/* Mobile: Always visible but smaller */
8428@media (max-width: 640px) {
8429    .prod-card-actions-btn {
8430        width: 28px;
8431        height: 28px;
8432        top: 6px;
8433        right: 6px;
8434        opacity: 95%;
8435    }
8436    
8437    .prod-card-actions-btn svg {
8438        width: 18px;
8439        height: 18px;
8440    }
8441}
8442
8443/* ===== Bottom Sheet Overlay ===== */
8444.pca-overlay {
8445    position: fixed;
8446    inset: 0;
8447    background: rgba(15, 23, 42, 0.6);
8448    backdrop-filter: blur(4px);
8449    z-index: 99998;
8450    opacity: 0;
8451    visibility: hidden;
8452    transition: all 0.3s ease;
8453}
8454
8455.pca-overlay.active {
8456    opacity: 1;
8457    visibility: visible;
8458}
8459
8460/* ===== Bottom Sheet Container ===== */
8461.pca-bottom-sheet {
8462    position: fixed;
8463    bottom: 0;
8464    left: 0;
8465    right: 0;
8466    background: #fff;
8467    border-radius: 20px 20px 0 0;
8468    box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.15);
8469    z-index: 99999;
8470    max-width: 600px;
8471    margin: 0 auto;
8472    transform: translateY(100%);
8473    transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
8474}
8475
8476.pca-bottom-sheet.active {
8477    transform: translateY(0);
8478}
8479
8480/* Drag Handle */
8481.pca-sheet-handle {
8482    width: 40px;
8483    height: 4px;
8484    background: #cbd5e1;
8485    border-radius: 999px;
8486    margin: 12px auto 8px;
8487}
8488
8489/* Product Preview */
8490.pca-sheet-product {
8491    display: flex;
8492    gap: 12px;
8493    padding: 12px 20px;
8494    border-bottom: 1px solid #f1f5f9;
8495}
8496
8497.pca-product-image {
8498    width: 60px;
8499    height: 60px;
8500    border-radius: 8px;
8501    object-fit: cover;
8502    flex-shrink: 0;
8503    border: 1px solid #e2e8f0;
8504}
8505
8506.pca-product-info {
8507    flex: 1;
8508    min-width: 0;
8509}
8510
8511.pca-product-name {
8512    font-size: 14px;
8513    font-weight: 600;
8514    color: #0f172a;
8515    margin: 0 0 4px 0;
8516    display: -webkit-box;
8517    -webkit-line-clamp: 2;
8518    -webkit-box-orient: vertical;
8519    overflow: hidden;
8520    line-height: 1.3;
8521}
8522
8523.pca-product-price {
8524    font-size: 15px;
8525    font-weight: 700;
8526    color: #EAB308;
8527    margin: 0;
8528}
8529
8530.pca-product-stock {
8531    font-size: 12px;
8532    color: #64748b;
8533    margin: 2px 0 0 0;
8534}
8535
8536.pca-product-stock.in {
8537    color: #16a34a;
8538}
8539
8540.pca-product-stock.low {
8541    color: #CA8A04;
8542}
8543
8544.pca-product-stock.out {
8545    color: #dc2626;
8546}
8547
8548/* Action Menu */
8549.pca-sheet-actions {
8550    padding: 8px 0 20px;
8551}
8552
8553.pca-action-item {
8554    display: flex;
8555    align-items: center;
8556    gap: 16px;
8557    padding: 16px 20px;
8558    background: transparent;
8559    border: 0;
8560    width: 100%;
8561    cursor: pointer;
8562    transition: background-color 0.15s ease;
8563    -webkit-tap-highlight-color: transparent;
8564}
8565
8566.pca-action-item:hover {
8567    background: #f8fafc;
8568}
8569
8570.pca-action-item:active {
8571    background: #f1f5f9;
8572}
8573
8574.pca-action-item:disabled {
8575    opacity: 0.4;
8576    cursor: not-allowed;
8577}
8578
8579.pca-action-item:disabled:hover {
8580    background: transparent;
8581}
8582
8583.pca-action-icon {
8584    width: 24px;
8585    height: 24px;
8586    color: #475569;
8587    flex-shrink: 0;
8588}
8589
8590.pca-action-content {
8591    flex: 1;
8592    text-align: left;
8593}
8594
8595.pca-action-label {
8596    font-size: 15px;
8597    font-weight: 600;
8598    color: #0f172a;
8599    margin: 0 0 2px 0;
8600}
8601
8602.pca-action-desc {
8603    font-size: 12px;
8604    color: #64748b;
8605    margin: 0;
8606}
8607
8608.pca-action-badge {
8609    display: inline-flex;
8610    align-items: center;
8611    justify-content: center;
8612    min-width: 20px;
8613    height: 20px;
8614    padding: 0 6px;
8615    background: #EAB308;
8616    color: #fff;
8617    font-size: 11px;
8618    font-weight: 700;
8619    border-radius: 999px;
8620    margin-left: auto;
8621}
8622
8623.pca-action-badge.compare {
8624    background: #CA8A04;
8625}
8626
8627/* Action Item Colors */
8628.pca-action-item.cart .pca-action-icon {
8629    color: #EAB308;
8630}
8631
8632.pca-action-item.compare .pca-action-icon {
8633    color: #CA8A04;
8634}
8635
8636.pca-action-item.compare.active {
8637    background: rgba(234, 179, 8, 0.05);
8638}
8639
8640.pca-action-item.compare.active .pca-action-label {
8641    color: #CA8A04;
8642}
8643
8644.pca-action-item.compare.active .pca-action-icon {
8645    color: #CA8A04;
8646}
8647
8648.pca-action-item.share .pca-action-icon {
8649    color: #64748b;
8650}
8651
8652/* Loading State */
8653.pca-action-item.loading {
8654    pointer-events: none;
8655}
8656
8657.pca-action-item.loading .pca-action-icon {
8658    animation: spin 1s linear infinite;
8659}
8660
8661@keyframes spin {
8662    from { transform: rotate(0deg); }
8663    to { transform: rotate(360deg); }
8664}
8665
8666/* Success State Animation */
8667.pca-action-item.success .pca-action-icon {
8668    animation: checkmark 0.5s ease;
8669}
8670
8671@keyframes checkmark {
8672    0% { transform: scale(0); }
8673    50% { transform: scale(1.2); }
8674    100% { transform: scale(1); }
8675}
8676
8677/* ===== Safe Area for Mobile Devices ===== */
8678@supports (padding-bottom: env(safe-area-inset-bottom)) {
8679    .pca-sheet-actions {
8680        padding-bottom: calc(20px + env(safe-area-inset-bottom));
8681    }
8682}
8683
8684/* ===== Responsive Tweaks ===== */
8685@media (max-width: 640px) {
8686    .pca-bottom-sheet {
8687        border-radius: 16px 16px 0 0;
8688    }
8689    
8690    .pca-sheet-product {
8691        padding: 10px 16px;
8692    }
8693    
8694    .pca-product-image {
8695        width: 50px;
8696        height: 50px;
8697    }
8698    
8699    .pca-product-name {
8700        font-size: 13px;
8701    }
8702    
8703    .pca-product-price {
8704        font-size: 14px;
8705    }
8706    
8707    .pca-action-item {
8708        padding: 14px 16px;
8709        gap: 12px;
8710    }
8711    
8712    .pca-action-icon {
8713        width: 22px;
8714        height: 22px;
8715    }
8716    
8717    .pca-action-label {
8718        font-size: 14px;
8719    }
8720}
8721
8722/* ===== Animation for Flying Product ===== */
8723.flying-product {
8724    position: fixed;
8725    pointer-events: none;
8726    z-index: 99999;
8727    animation: fadeOut 0.8s ease forwards;
8728}
8729
8730@keyframes fadeOut {
8731    to {
8732        opacity: 0;
8733        transform: scale(0.5);
8734    }
8735}
8736
8737/* ===== Desktop Dropdown (Alternative) ===== */
8738@media (min-width: 1024px) {
8739    /* Optional: Could add desktop dropdown menu here */
8740    /* For now, using bottom sheet for consistency */
8741}
8742
8743/* ===== Print Styles ===== */
8744@media print {
8745    .prod-card-actions-btn,
8746    .pca-overlay,
8747    .pca-bottom-sheet {
8748        display: none !important;
8749    }
8750}
8751
8752</style>
8753  <style data-inlined="modal.css">
8754/* Modern Modal Dialog for Cart Operations */
8755.modal-overlay {
8756  position: fixed;
8757  top: 0;
8758  left: 0;
8759  right: 0;
8760  bottom: 0;
8761  background: rgba(0, 0, 0, 0.5);
8762  backdrop-filter: blur(4px);
8763  display: flex;
8764  align-items: center;
8765  justify-content: center;
8766  z-index: 9999;
8767  opacity: 0;
8768  visibility: hidden;
8769  transition: all 0.2s ease;
8770}
8771
8772.modal-overlay.show {
8773  opacity: 1;
8774  visibility: visible;
8775}
8776
8777.modal-dialog {
8778  background: white;
8779  border-radius: 16px;
8780  box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
8781  max-width: 400px;
8782  width: 90%;
8783  max-height: 90vh;
8784  overflow-y: auto;
8785  transform: scale(0.95);
8786  transition: transform 0.2s ease;
8787}
8788
8789.modal-overlay.show .modal-dialog {
8790  transform: scale(1);
8791}
8792
8793.modal-header {
8794  padding: 24px 24px 16px;
8795  border-bottom: 1px solid #f1f5f9;
8796}
8797
8798.modal-title {
8799  font-size: 20px;
8800  font-weight: 700;
8801  color: #0f172a;
8802  margin: 0;
8803  display: flex;
8804  align-items: center;
8805  gap: 12px;
8806}
8807
8808.modal-body {
8809  padding: 16px 24px 24px;
8810}
8811
8812.modal-message {
8813  font-size: 16px;
8814  line-height: 1.5;
8815  color: #475569;
8816  margin: 0;
8817}
8818
8819.modal-actions {
8820  display: flex;
8821  gap: 12px;
8822  margin-top: 24px;
8823}
8824
8825.modal-btn {
8826  flex: 1;
8827  padding: 12px 16px;
8828  border: none;
8829  border-radius: 8px;
8830  font-size: 14px;
8831  font-weight: 600;
8832  cursor: pointer;
8833  transition: all 0.2s ease;
8834  text-align: center;
8835}
8836
8837.modal-btn-secondary {
8838  background: #f8fafc;
8839  color: #64748b;
8840  border: 1px solid #e2e8f0;
8841}
8842
8843.modal-btn-secondary:hover {
8844  background: #f1f5f9;
8845  border-color: #cbd5e1;
8846}
8847
8848.modal-btn-danger {
8849  background: #ef4444;
8850  color: white;
8851}
8852
8853.modal-btn-danger:hover {
8854  background: #dc2626;
8855}
8856
8857.modal-btn-primary {
8858  background: #EAB308;
8859  color: white;
8860}
8861
8862.modal-btn-primary:hover {
8863  background: #CA8A04;
8864}
8865
8866.modal-icon {
8867  font-size: 24px;
8868  width: 32px;
8869  height: 32px;
8870  display: flex;
8871  align-items: center;
8872  justify-content: center;
8873  border-radius: 8px;
8874}
8875
8876.modal-icon.danger {
8877  background: #fef2f2;
8878  color: #ef4444;
8879}
8880
8881.modal-icon.warning {
8882  background: #FFFBEB;
8883  color: #CA8A04;
8884}
8885
8886.modal-icon.success {
8887  background: #f0fdf4;
8888  color: #22c55e;
8889}
8890
8891.modal-icon.info {
8892  background: #FFFBEB;
8893  color: #EAB308;
8894}
8895
8896/* Mobile responsive */
8897@media (max-width: 640px) {
8898  .modal-dialog {
8899    margin: 16px;
8900    max-width: none;
8901    width: auto;
8902  }
8903  
8904  .modal-header,
8905  .modal-body {
8906    padding-left: 20px;
8907    padding-right: 20px;
8908  }
8909  
8910  .modal-actions {
8911    flex-direction: column;
8912  }
8913  
8914  .modal-btn {
8915    flex: none;
8916  }
8917}
8918</style>
8919      
8920        <style data-inlined="reviews-points.css">
8921/**
8922 * Reviews & Points System - Styles
8923 * Product reviews, rating display, and point redemption UI
8924 */
8925
8926/* ===================================
8927   PRODUCT REVIEWS
8928   =================================== */
8929
8930.product-tabs {
8931  display: flex;
8932  border-bottom: 2px solid #e5e7eb;
8933  margin: 24px 0 16px;
8934  gap: 8px;
8935}
8936
8937.tab-btn {
8938  padding: 12px 20px;
8939  border: none;
8940  background: none;
8941  cursor: pointer;
8942  font-size: 15px;
8943  font-weight: 500;
8944  color: #6b7280;
8945  border-bottom: 3px solid transparent;
8946  transition: all 0.2s ease;
8947  position: relative;
8948  top: 2px;
8949}
8950
8951.tab-btn:hover {
8952  color: #111827;
8953}
8954
8955.tab-btn.active {
8956  color: #CA8A04;
8957  border-bottom-color: #CA8A04;
8958}
8959
8960.tab-content {
8961  padding: 20px 0;
8962}
8963
8964/* Review Statistics */
8965.review-stats {
8966  display: grid;
8967  grid-template-columns: 200px 1fr;
8968  gap: 32px;
8969  padding: 24px;
8970  background: #f9fafb;
8971  border-radius: 12px;
8972  margin-bottom: 24px;
8973}
8974
8975.rating-summary {
8976  text-align: center;
8977}
8978
8979.avg-rating {
8980  display: block;
8981  font-size: 48px;
8982  font-weight: 700;
8983  color: #111827;
8984  line-height: 1;
8985}
8986
8987.rating-summary .stars {
8988  font-size: 24px;
8989  color: #EAB308;
8990  margin: 8px 0;
8991}
8992
8993.total-reviews {
8994  display: block;
8995  font-size: 14px;
8996  color: #6b7280;
8997  margin-top: 4px;
8998}
8999
9000.rating-breakdown {
9001  display: flex;
9002  flex-direction: column;
9003  gap: 8px;
9004}
9005
9006.rating-bar {
9007  display: flex;
9008  align-items: center;
9009  gap: 12px;
9010}
9011
9012.rating-label {
9013  font-size: 14px;
9014  color: #374151;
9015  width: 32px;
9016  text-align: right;
9017}
9018
9019.bar-container {
9020  flex: 1;
9021  height: 8px;
9022  background: #e5e7eb;
9023  border-radius: 4px;
9024  overflow: hidden;
9025}
9026
9027.bar-fill {
9028  height: 100%;
9029  background: linear-gradient(90deg, #EAB308 0%, #CA8A04 100%);
9030  transition: width 0.3s ease;
9031}
9032
9033.rating-count {
9034  font-size: 14px;
9035  color: #6b7280;
9036  width: 40px;
9037  text-align: right;
9038}
9039
9040/* Review Filters */
9041.review-filters {
9042  display: flex;
9043  justify-content: space-between;
9044  align-items: center;
9045  margin-bottom: 20px;
9046}
9047
9048.review-sort {
9049  padding: 8px 12px;
9050  border: 1px solid #d1d5db;
9051  border-radius: 8px;
9052  font-size: 14px;
9053  background: white;
9054}
9055
9056.write-review-btn {
9057  padding: 10px 20px;
9058  background: #CA8A04;
9059  color: white;
9060  border: none;
9061  border-radius: 8px;
9062  font-weight: 500;
9063  cursor: pointer;
9064  transition: background 0.2s;
9065}
9066
9067.write-review-btn:hover {
9068  background: #B45309;
9069}
9070
9071/* Review List */
9072.reviews-list {
9073  display: flex;
9074  flex-direction: column;
9075  gap: 16px;
9076}
9077
9078.review-item {
9079  padding: 20px;
9080  background: white;
9081  border: 1px solid #e5e7eb;
9082  border-radius: 12px;
9083  transition: box-shadow 0.2s;
9084}
9085
9086.review-item:hover {
9087  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
9088}
9089
9090.review-header {
9091  display: flex;
9092  justify-content: space-between;
9093  align-items: flex-start;
9094  margin-bottom: 8px;
9095}
9096
9097.review-author {
9098  display: flex;
9099  align-items: center;
9100  gap: 8px;
9101}
9102
9103.review-author strong {
9104  font-size: 15px;
9105  color: #111827;
9106}
9107
9108.verified-badge {
9109  display: inline-flex;
9110  align-items: center;
9111  gap: 4px;
9112  font-size: 12px;
9113  color: #059669;
9114  background: #d1fae5;
9115  padding: 2px 8px;
9116  border-radius: 4px;
9117  font-weight: 500;
9118}
9119
9120.anonymous-badge {
9121  display: inline-flex;
9122  align-items: center;
9123  font-size: 12px;
9124  color: #6b7280;
9125  background: #f3f4f6;
9126  padding: 2px 6px;
9127  border-radius: 4px;
9128  margin-left: 4px;
9129  cursor: help;
9130}
9131
9132.review-rating .stars {
9133  font-size: 16px;
9134  color: #EAB308;
9135}
9136
9137.review-date {
9138  font-size: 13px;
9139  color: #9ca3af;
9140  margin-bottom: 12px;
9141}
9142
9143.review-text {
9144  font-size: 15px;
9145  line-height: 1.6;
9146  color: #374151;
9147  margin-bottom: 12px;
9148}
9149
9150.review-images {
9151  display: flex;
9152  gap: 8px;
9153  margin-bottom: 12px;
9154  flex-wrap: wrap;
9155}
9156
9157.review-img {
9158  width: 80px;
9159  height: 80px;
9160  object-fit: cover;
9161  border-radius: 8px;
9162  cursor: pointer;
9163  transition: transform 0.2s;
9164}
9165
9166.review-img:hover {
9167  transform: scale(1.05);
9168}
9169
9170.review-actions {
9171  display: flex;
9172  gap: 12px;
9173  padding-top: 12px;
9174  border-top: 1px solid #f3f4f6;
9175}
9176
9177.btn-helpful {
9178  display: flex;
9179  align-items: center;
9180  gap: 6px;
9181  padding: 6px 12px;
9182  background: #f9fafb;
9183  border: 1px solid #e5e7eb;
9184  border-radius: 6px;
9185  font-size: 13px;
9186  color: #6b7280;
9187  cursor: pointer;
9188  transition: all 0.2s;
9189}
9190
9191.btn-helpful:hover {
9192  background: #f3f4f6;
9193  color: #111827;
9194  border-color: #d1d5db;
9195}
9196
9197.btn-helpful.voted {
9198  background: #FEF3C7;
9199  color: #B45309;
9200  border-color: #FCD34D;
9201}
9202
9203.helpful-icon {
9204  font-size: 14px;
9205}
9206
9207.no-reviews {
9208  text-align: center;
9209  padding: 60px 20px;
9210  color: #9ca3af;
9211  font-size: 15px;
9212}
9213
9214.load-more-btn {
9215  display: block;
9216  margin: 24px auto 0;
9217  padding: 12px 32px;
9218  background: white;
9219  border: 1px solid #d1d5db;
9220  border-radius: 8px;
9221  font-size: 14px;
9222  font-weight: 500;
9223  color: #374151;
9224  cursor: pointer;
9225  transition: all 0.2s;
9226}
9227
9228.load-more-btn:hover {
9229  background: #f9fafb;
9230  border-color: #9ca3af;
9231}
9232
9233/* ===================================
9234   POINT BALANCE WIDGET (LEGACY - kept for backward compatibility)
9235   =================================== */
9236
9237.point-balance-card {
9238  background: linear-gradient(135deg, #EAB308 0%, #8B6914 100%);
9239  color: white;
9240  padding: 24px;
9241  border-radius: 16px;
9242  margin-bottom: 24px;
9243  box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
9244}
9245
9246.point-header {
9247  display: flex;
9248  justify-content: space-between;
9249  align-items: center;
9250  margin-bottom: 12px;
9251}
9252
9253.point-header h3 {
9254  font-size: 18px;
9255  font-weight: 600;
9256  margin: 0;
9257  display: flex;
9258  align-items: center;
9259  gap: 8px;
9260}
9261
9262.point-header .balance {
9263  font-size: 32px;
9264  font-weight: 700;
9265}
9266
9267.point-value {
9268  font-size: 14px;
9269  opacity: 0.9;
9270  margin: 8px 0 16px;
9271}
9272
9273.point-stats {
9274  display: flex;
9275  gap: 20px;
9276  font-size: 13px;
9277  opacity: 0.85;
9278  margin-bottom: 16px;
9279}
9280
9281.btn-view-history {
9282  display: inline-block;
9283  padding: 10px 20px;
9284  background: rgba(255, 255, 255, 0.2);
9285  color: white;
9286  text-decoration: none;
9287  border-radius: 8px;
9288  font-size: 14px;
9289  font-weight: 500;
9290  transition: background 0.2s;
9291}
9292
9293.btn-view-history:hover {
9294  background: rgba(255, 255, 255, 0.3);
9295}
9296
9297/* ===================================
9298   CHECKOUT POINT REDEMPTION
9299   =================================== */
9300
9301.checkout-points {
9302  padding: 16px;
9303  background: #FEF3C7;
9304  border: 2px solid #EAB308;
9305  border-radius: 12px;
9306  margin-bottom: 16px;
9307}
9308
9309.points-header {
9310  display: flex;
9311  align-items: center;
9312  gap: 10px;
9313  margin-bottom: 12px;
9314}
9315
9316.points-header input[type="checkbox"] {
9317  width: 18px;
9318  height: 18px;
9319  cursor: pointer;
9320}
9321
9322.points-header label {
9323  font-size: 15px;
9324  color: #78350F;
9325  cursor: pointer;
9326  margin: 0;
9327}
9328
9329#points-slider {
9330  padding: 16px;
9331  background: white;
9332  border-radius: 8px;
9333  margin-top: 12px;
9334}
9335
9336#points-slider label {
9337  display: block;
9338  font-size: 14px;
9339  color: #374151;
9340  margin-bottom: 8px;
9341  font-weight: 500;
9342}
9343
9344#points-amount {
9345  width: 100%;
9346  margin-bottom: 8px;
9347}
9348
9349#points-display {
9350  display: flex;
9351  justify-content: space-between;
9352  font-size: 14px;
9353  color: #111827;
9354  margin-top: 8px;
9355}
9356
9357#points-display .discount-amount {
9358  color: #059669;
9359  font-weight: 600;
9360}
9361
9362.price-row.point-discount {
9363  color: #059669;
9364  font-weight: 500;
9365}
9366
9367/* ===================================
9368   REVIEWABLE ORDERS
9369   =================================== */
9370
9371.reviewable-orders {
9372  background: white;
9373  border: 1px solid #e5e7eb;
9374  border-radius: 12px;
9375  padding: 20px;
9376  margin-bottom: 24px;
9377}
9378
9379.reviewable-orders h3 {
9380  font-size: 18px;
9381  margin-bottom: 16px;
9382  color: #111827;
9383}
9384
9385.order-card {
9386  display: flex;
9387  gap: 16px;
9388  padding: 16px;
9389  background: #f9fafb;
9390  border-radius: 8px;
9391  margin-bottom: 12px;
9392}
9393
9394.order-card:last-child {
9395  margin-bottom: 0;
9396}
9397
9398.order-card img {
9399  width: 80px;
9400  height: 80px;
9401  object-fit: cover;
9402  border-radius: 8px;
9403}
9404
9405.order-info {
9406  flex: 1;
9407}
9408
9409.order-info h4 {
9410  font-size: 15px;
9411  margin: 0 0 4px;
9412  color: #111827;
9413}
9414
9415.order-info p {
9416  font-size: 13px;
9417  color: #6b7280;
9418  margin: 0 0 12px;
9419}
9420
9421.btn-review {
9422  padding: 8px 16px;
9423  background: #059669;
9424  color: white;
9425  border: none;
9426  border-radius: 6px;
9427  font-size: 14px;
9428  font-weight: 500;
9429  cursor: pointer;
9430  transition: background 0.2s;
9431}
9432
9433.btn-review:hover {
9434  background: #047857;
9435}
9436
9437/* ===================================
9438   TOAST NOTIFICATIONS
9439   =================================== */
9440
9441.review-toast {
9442  position: fixed;
9443  bottom: 24px;
9444  right: 24px;
9445  padding: 14px 20px;
9446  background: #111827;
9447  color: white;
9448  border-radius: 8px;
9449  font-size: 14px;
9450  line-height: 1.4;
9451  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
9452  transform: translateY(100px);
9453  opacity: 0;
9454  transition: all 0.3s ease;
9455  z-index: 10000;
9456  max-width: 400px;
9457  word-wrap: break-word;
9458}
9459
9460.review-toast.show {
9461  transform: translateY(0);
9462  opacity: 1;
9463}
9464
9465/* Mobile: Position above bottom-nav */
9466@media (max-width: 768px) {
9467  .review-toast {
9468    bottom: 80px; /* Above bottom-nav (50px height + padding + safe margin) */
9469    right: 16px;
9470    left: 16px;
9471    max-width: calc(100% - 32px);
9472  }
9473}
9474
9475.review-toast.toast-success {
9476  background: #059669;
9477}
9478
9479.review-toast.toast-error {
9480  background: #dc2626;
9481}
9482
9483.review-toast.toast-info {
9484  background: #CA8A04;
9485}
9486
9487/* ===================================
9488   RESPONSIVE
9489   =================================== */
9490
9491@media (max-width: 768px) {
9492  .point-balance-card {
9493    border-radius: 0;
9494  }
9495  
9496  .review-stats {
9497    grid-template-columns: 70px 1fr;
9498    gap: 20px;
9499    padding: 16px;
9500  }
9501
9502  .rating-summary {
9503    text-align: center;
9504  }
9505  
9506  .rating-summary .avg-rating {
9507    font-size: 32px;
9508  }
9509  
9510  .rating-summary .stars {
9511    font-size: 18px;
9512    margin: 4px 0;
9513  }
9514  
9515  .total-reviews {
9516    font-size: 11px;
9517  }
9518  
9519  /* Rating breakdown - more compact */
9520  .rating-breakdown {
9521    gap: 2px;
9522  }
9523  
9524  .rating-breakdown .rating-bar {
9525    gap: 4px;
9526  }
9527  
9528  .rating-breakdown .rating-label {
9529    font-size: 11px;
9530    width: 16px;
9531    min-width: 16px;
9532    text-align: left;
9533  }
9534  
9535  .rating-breakdown .bar-container {
9536    height: 6px;
9537  }
9538  
9539  .rating-breakdown .rating-count {
9540    font-size: 11px;
9541    width: 16px;
9542    min-width: 16px;
9543  }
9544  
9545  .review-header {
9546    flex-direction: column;
9547    gap: 8px;
9548    align-items: flex-start;
9549  }
9550  
9551  .review-rating {
9552    order: -1;
9553    margin-bottom: 4px;
9554  }
9555  
9556  .review-rating .stars {
9557    font-size: 14px;
9558  }
9559
9560  .review-filters {
9561    flex-direction: column;
9562    gap: 12px;
9563    align-items: stretch;
9564  }
9565
9566  .review-sort {
9567    width: 100%;
9568  }
9569
9570  .point-header .balance {
9571    font-size: 24px;
9572  }
9573
9574  .point-stats {
9575    flex-direction: column;
9576    gap: 8px;
9577  }
9578
9579  .order-card {
9580    flex-direction: column;
9581  }
9582
9583  .order-card img {
9584    width: 100%;
9585    height: 200px;
9586  }
9587}
9588
9589/* ===================================
9590   LOADING & ERROR STATES
9591   =================================== */
9592
9593.review-loading {
9594  text-align: center;
9595  padding: 40px;
9596  color: #9ca3af;
9597  font-size: 14px;
9598}
9599
9600.review-error {
9601  text-align: center;
9602  padding: 40px;
9603  color: #dc2626;
9604  font-size: 14px;
9605  background: #fee2e2;
9606  border-radius: 8px;
9607}
9608
9609.review-empty {
9610  text-align: center;
9611  padding: 10px;
9612  color: #6b7280;
9613  font-size: 14px;
9614}
9615
9616/* ===================================
9617   REVIEWABLE ORDERS LIST
9618   =================================== */
9619
9620.reviewable-orders-list {
9621  display: flex;
9622  flex-direction: column;
9623  gap: 16px;
9624}
9625
9626.reviewable-order-item {
9627  display: grid;
9628  grid-template-columns: 80px 1fr auto;
9629  gap: 16px;
9630  padding: 16px;
9631  background: #fff;
9632  border: 1px solid #e5e7eb;
9633  border-radius: 12px;
9634  align-items: center;
9635  transition: box-shadow 0.2s;
9636}
9637
9638.reviewable-order-item:hover {
9639  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
9640}
9641
9642.reviewable-order-item .roi-image {
9643  width: 80px;
9644  height: 80px;
9645  object-fit: cover;
9646  border-radius: 8px;
9647  border: 1px solid #e5e7eb;
9648}
9649
9650.reviewable-order-item .roi-info {
9651  display: flex;
9652  flex-direction: column;
9653  gap: 4px;
9654}
9655
9656.reviewable-order-item .roi-info h4 {
9657  margin: 0;
9658  font-size: 15px;
9659  font-weight: 600;
9660  color: #111827;
9661  line-height: 1.4;
9662}
9663
9664.reviewable-order-item .roi-meta {
9665  margin: 0;
9666  font-size: 13px;
9667  color: #6b7280;
9668}
9669
9670.reviewable-order-item .btn-write-review {
9671  white-space: nowrap;
9672  padding: 10px 20px;
9673  font-size: 14px;
9674}
9675
9676@media (max-width: 640px) {
9677  .reviewable-order-item {
9678    grid-template-columns: 60px 1fr;
9679    gap: 12px;
9680  }
9681  
9682  .reviewable-order-item .roi-image {
9683    width: 60px;
9684    height: 60px;
9685  }
9686  
9687  .reviewable-order-item .btn-write-review {
9688    grid-column: 1 / -1;
9689    width: 100%;
9690  }
9691}
9692
9693/* ===================================
9694   REVIEW SUBMISSION MODAL
9695   =================================== */
9696
9697/* Modal Overlay Background */
9698#review-modal-overlay {
9699  position: fixed;
9700  top: 0;
9701  left: 0;
9702  right: 0;
9703  bottom: 0;
9704  background: rgba(0, 0, 0, 0.6) !important;
9705  backdrop-filter: blur(8px);
9706  display: flex;
9707  align-items: center;
9708  justify-content: center;
9709  z-index: 99999 !important;
9710  padding: 20px;
9711}
9712
9713.review-modal {
9714  max-width: 600px !important;
9715  width: 90% !important;
9716  background: white;
9717  border-radius: 16px !important;
9718  box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
9719  max-height: 90vh;
9720  display: flex;
9721  flex-direction: column;
9722  overflow: hidden !important;
9723}
9724
9725#review-modal-overlay .modal-content,
9726.review-modal .modal-content {
9727  padding: 0 !important;
9728  flex: initial !important;
9729  overflow-y: visible !important;
9730}
9731
9732.review-modal .modal-header {
9733  display: flex;
9734  justify-content: space-between;
9735  align-items: center;
9736  padding: 20px 24px;
9737  border-bottom: 1px solid #e5e7eb;
9738}
9739
9740.review-modal .modal-title {
9741  margin: 0;
9742  font-size: 20px;
9743  font-weight: 700;
9744  color: #111827;
9745}
9746
9747.review-modal .modal-close {
9748  background: none;
9749  border: none;
9750  cursor: pointer;
9751  padding: 4px;
9752  color: #6b7280;
9753  transition: color 0.2s;
9754  display: flex;
9755  align-items: center;
9756  justify-content: center;
9757}
9758
9759.review-modal .modal-close:hover {
9760  color: #111827;
9761}
9762
9763.review-modal .modal-body {
9764  padding: 24px;
9765  max-height: calc(90vh - 140px);
9766  overflow-y: auto;
9767}
9768
9769/* Custom Scrollbar for Modal */
9770.review-modal .modal-body::-webkit-scrollbar {
9771  width: 8px;
9772}
9773
9774.review-modal .modal-body::-webkit-scrollbar-track {
9775  background: #f1f5f9;
9776  border-radius: 4px;
9777}
9778
9779.review-modal .modal-body::-webkit-scrollbar-thumb {
9780  background: linear-gradient(180deg, #EAB308 0%, #CA8A04 100%);
9781  border-radius: 4px;
9782}
9783
9784.review-modal .modal-body::-webkit-scrollbar-thumb:hover {
9785  background: linear-gradient(180deg, #CA8A04 0%, #B45309 100%);
9786}
9787
9788/* Firefox Scrollbar */
9789.review-modal .modal-body {
9790  scrollbar-width: thin;
9791  scrollbar-color: #EAB308 #f1f5f9;
9792}
9793
9794.review-modal .modal-footer {
9795  display: flex;
9796  gap: 12px;
9797  margin-top: 24px;
9798  padding-top: 20px;
9799  border-top: 1px solid #f1f5f9;
9800}
9801
9802.review-product-info {
9803  display: flex;
9804  gap: 16px;
9805  padding: 16px;
9806  background: #f9fafb;
9807  border-radius: 8px;
9808  margin-bottom: 24px;
9809}
9810
9811.review-product-thumb {
9812  width: 80px;
9813  height: 80px;
9814  object-fit: cover;
9815  border-radius: 6px;
9816  border: 1px solid #e5e7eb;
9817}
9818
9819.review-product-details h4 {
9820  margin: 0 0 8px 0;
9821  font-size: 16px;
9822  font-weight: 600;
9823  color: #111827;
9824}
9825
9826.review-product-details p {
9827  margin: 0;
9828  font-size: 13px;
9829  color: #6b7280;
9830}
9831
9832/* Star Rating Input */
9833.star-rating-input {
9834  display: inline-flex;
9835  gap: 8px;
9836  margin-right: 12px;
9837  vertical-align: middle;
9838}
9839
9840.star-btn {
9841  background: none;
9842  border: none;
9843  font-size: 32px;
9844  color: #d1d5db;
9845  cursor: pointer;
9846  transition: all 0.2s;
9847  padding: 0;
9848  line-height: 1;
9849}
9850
9851.star-btn:hover,
9852.star-btn.active {
9853  color: #EAB308;
9854  transform: scale(1.1);
9855}
9856
9857.rating-label {
9858  display: inline-block;
9859  font-size: 15px;
9860  color: #6b7280;
9861  margin: 0;
9862  vertical-align: middle;
9863  min-width: 150px;
9864}
9865
9866/* Form Elements */
9867.review-form .form-group {
9868  margin-bottom: 20px;
9869}
9870
9871.review-form .form-label {
9872  display: block;
9873  font-size: 14px;
9874  font-weight: 600;
9875  color: #374151;
9876  margin-bottom: 8px;
9877}
9878
9879.review-form .form-control {
9880  width: 100%;
9881  padding: 12px;
9882  border: 1px solid #d1d5db;
9883  border-radius: 8px;
9884  font-size: 14px;
9885  font-family: inherit;
9886  transition: border-color 0.2s;
9887}
9888
9889.review-form .form-control:focus {
9890  outline: none;
9891  border-color: #CA8A04;
9892  box-shadow: 0 0 0 3px rgba(202, 138, 4, 0.1);
9893}
9894
9895.review-form .form-hint {
9896  font-size: 13px;
9897  color: #6b7280;
9898  margin-top: 8px;
9899}
9900
9901/* Image Upload */
9902.btn-upload-trigger {
9903  display: inline-flex;
9904  align-items: center;
9905  gap: 8px;
9906  padding: 10px 16px;
9907  background: #f9fafb;
9908  border: 2px dashed #d1d5db;
9909  border-radius: 8px;
9910  color: #374151;
9911  cursor: pointer;
9912  font-size: 14px;
9913  font-weight: 500;
9914  transition: all 0.2s;
9915}
9916
9917.btn-upload-trigger:hover {
9918  background: #f3f4f6;
9919  border-color: #CA8A04;
9920  color: #CA8A04;
9921}
9922
9923.image-preview-grid {
9924  display: grid;
9925  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
9926  gap: 12px;
9927  margin-top: 16px;
9928}
9929
9930.image-preview-item {
9931  position: relative;
9932  aspect-ratio: 1;
9933  border-radius: 8px;
9934  overflow: hidden;
9935  border: 1px solid #e5e7eb;
9936}
9937
9938.image-preview-item img {
9939  width: 100%;
9940  height: 100%;
9941  object-fit: cover;
9942}
9943
9944.remove-image {
9945  position: absolute;
9946  top: 4px;
9947  right: 4px;
9948  background: rgba(0, 0, 0, 0.7);
9949  border: none;
9950  color: white;
9951  width: 24px;
9952  height: 24px;
9953  border-radius: 50%;
9954  cursor: pointer;
9955  display: flex;
9956  align-items: center;
9957  justify-content: center;
9958  transition: background 0.2s;
9959}
9960
9961.remove-image:hover {
9962  background: rgba(0, 0, 0, 0.9);
9963}
9964
9965/* Point Reward Info */
9966.point-reward-info {
9967  background: linear-gradient(135deg, #FEF3C7 0%, #FDE68A 100%);
9968  border: 2px solid #EAB308;
9969  border-radius: 12px;
9970  padding: 16px;
9971  margin: 20px 0;
9972}
9973
9974.pri-header {
9975  display: flex;
9976  align-items: center;
9977  gap: 8px;
9978  font-size: 16px;
9979  font-weight: 700;
9980  color: #8B6914;
9981  margin-bottom: 12px;
9982}
9983
9984.pri-list {
9985  list-style: none;
9986  padding: 0;
9987  margin: 0 0 12px 0;
9988}
9989
9990.pri-list li {
9991  padding: 6px 0;
9992  font-size: 14px;
9993  color: #78350F;
9994  transition: opacity 0.2s;
9995}
9996
9997.pri-total {
9998  padding-top: 12px;
9999  border-top: 2px solid rgba(234, 179, 8, 0.3);
10000  font-size: 16px;
10001  color: #8B6914;
10002  font-weight: 600;
10003}
10004
10005.form-error {
10006  background: #fee2e2;
10007  border: 1px solid #f87171;
10008  color: #991b1b;
10009  padding: 12px;
10010  border-radius: 8px;
10011  font-size: 14px;
10012  margin-top: 16px;
10013}
10014
10015/* Buttons */
10016.review-form .btn {
10017  padding: 12px 24px;
10018  border: none;
10019  border-radius: 8px;
10020  font-size: 15px;
10021  font-weight: 600;
10022  cursor: pointer;
10023  transition: all 0.2s;
10024}
10025
10026.review-form .btn-primary {
10027  background: #CA8A04;
10028  color: white;
10029}
10030
10031.review-form .btn-primary:hover {
10032  background: #B45309;
10033}
10034
10035.review-form .btn-primary:disabled {
10036  background: #94a3b8;
10037  cursor: not-allowed;
10038}
10039
10040.review-form .btn-secondary {
10041  background: #f1f5f9;
10042  color: #64748b;
10043}
10044
10045.review-form .btn-secondary:hover {
10046  background: #e2e8f0;
10047  color: #475569;
10048}
10049
10050@media (max-width: 768px) {
10051    #review-modal-overlay { padding: 0; }
10052    .review-modal { max-width: 100%; width: 100% !important; }
10053}
10054
10055/* ===================================
10056   POINT SUMMARY GRID (Profile Page)
10057   =================================== */
10058
10059.point-summary-grid {
10060  display: grid;
10061  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
10062  gap: 12px;
10063  margin-bottom: 32px;
10064}
10065
10066.point-summary-card {
10067  background: linear-gradient(135deg, #f9fafb 0%, #ffffff 100%);
10068  border: 1px solid #e5e7eb;
10069  border-radius: 12px;
10070  padding: 16px 20px;
10071  display: flex;
10072  align-items: center;
10073  justify-content: space-between;
10074  gap: 12px;
10075  transition: all 0.2s;
10076}
10077
10078.point-summary-card:hover {
10079  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
10080  transform: translateY(-2px);
10081}
10082
10083.point-summary-card .psc-left {
10084  display: flex;
10085  flex-direction: column;
10086  gap: 2px;
10087}
10088
10089.point-summary-card .psc-icon {
10090  font-size: 24px;
10091  margin-bottom: 4px;
10092}
10093
10094.point-summary-card .psc-label {
10095  font-size: 13px;
10096  color: #6b7280;
10097  font-weight: 500;
10098  text-transform: uppercase;
10099  letter-spacing: 0.5px;
10100}
10101
10102.point-summary-card .psc-value {
10103  font-size: 26px;
10104  font-weight: 700;
10105  color: #111827;
10106  flex-shrink: 0;
10107}
10108
10109/* Point History Section */
10110.point-history-section {
10111  margin-top: 32px;
10112}
10113
10114.point-history-section h3 {
10115  font-size: 18px;
10116  font-weight: 600;
10117  color: #111827;
10118  margin-bottom: 16px;
10119}
10120
10121.point-history-list {
10122  display: flex;
10123  flex-direction: column;
10124  gap: 12px;
10125}
10126
10127.point-history-item {
10128  display: grid;
10129  grid-template-columns: 40px 1fr auto;
10130  gap: 16px;
10131  padding: 16px;
10132  background: white;
10133  border: 1px solid #e5e7eb;
10134  border-radius: 10px;
10135  align-items: center;
10136  transition: all 0.2s;
10137}
10138
10139.point-history-item:hover {
10140  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
10141}
10142
10143.point-history-item.credit {
10144  border-left: 3px solid #10b981;
10145}
10146
10147.point-history-item.debit {
10148  border-left: 3px solid #CA8A04;
10149}
10150
10151.point-history-item .phi-icon {
10152  width: 40px;
10153  height: 40px;
10154  display: flex;
10155  align-items: center;
10156  justify-content: center;
10157  font-size: 20px;
10158  border-radius: 50%;
10159  background: #f9fafb;
10160}
10161
10162.point-history-item.credit .phi-icon {
10163  background: #d1fae5;
10164}
10165
10166.point-history-item.debit .phi-icon {
10167  background: #FEF3C7;
10168}
10169
10170.point-history-item .phi-details {
10171  display: flex;
10172  flex-direction: column;
10173  gap: 4px;
10174}
10175
10176.point-history-item .phi-desc {
10177  font-size: 15px;
10178  font-weight: 500;
10179  color: #111827;
10180}
10181
10182.point-history-item .phi-date {
10183  font-size: 13px;
10184  color: #9ca3af;
10185}
10186
10187.point-history-item .phi-amount {
10188  font-size: 18px;
10189  font-weight: 700;
10190}
10191
10192.point-history-item .phi-amount.credit {
10193  color: #10b981;
10194}
10195
10196.point-history-item .phi-amount.debit {
10197  color: #CA8A04;
10198}
10199
10200@media (max-width: 640px) {
10201  .point-summary-grid {
10202    grid-template-columns: 1fr;
10203  }
10204  
10205  .point-summary-card .psc-value {
10206    font-size: 22px;
10207  }
10208
10209  .point-history-item {
10210    grid-template-columns: 36px 1fr;
10211    gap: 12px;
10212  }
10213  
10214  .point-history-item .phi-amount {
10215    grid-column: 2;
10216    text-align: right;
10217    margin-top: 8px;
10218  }
10219}
10220
10221/* ── Pending points: compact widget indicator ── */
10222.pbc-pending {
10223  font-size: 12px;
10224  opacity: 0.85;
10225  margin-top: 4px;
10226  color: #FDE68A;
10227}
10228
10229/* ── Pending points: summary card ── */
10230.point-summary-card.psc-pending-card {
10231  background: linear-gradient(135deg, #FFFBEB 0%, #FEF3C7 100%);
10232  border-color: #FBBF24;
10233}
10234
10235.point-summary-card.psc-pending-card .psc-value {
10236  color: #78350F;
10237}
10238
10239.point-summary-card .psc-sub {
10240  font-size: 11px;
10241  color: #9ca3af;
10242  margin-top: 4px;
10243}
10244
10245/* ── Pending points: history item ── */
10246.point-history-item.phi-pending {
10247  opacity: 0.75;
10248  border-left-color: #FBBF24;
10249  background: #FFFBEB;
10250}
10251
10252.point-history-item.phi-pending .phi-icon {
10253  background: #FEF3C7;
10254}
10255
10256.phi-pending-badge {
10257  display: inline-block;
10258  font-size: 11px;
10259  font-weight: 600;
10260  padding: 2px 7px;
10261  border-radius: 10px;
10262  background: #FEF3C7;
10263  color: #8B6914;
10264  border: 1px solid #FBBF24;
10265  margin-left: 8px;
10266  vertical-align: middle;
10267}
10268
10269/* ── Reversed points: history item ── */
10270.point-history-item.phi-reversed {
10271  opacity: 0.55;
10272  border-left-color: #d1d5db;
10273  background: #f9fafb;
10274}
10275
10276.point-history-item.phi-reversed .phi-icon {
10277  background: #f3f4f6;
10278}
10279
10280.point-history-item.phi-reversed .phi-amount {
10281  text-decoration: line-through;
10282  color: #9ca3af !important;
10283}
10284
10285.phi-reversed-badge {
10286  display: inline-block;
10287  font-size: 11px;
10288  font-weight: 600;
10289  padding: 2px 7px;
10290  border-radius: 10px;
10291  background: #fee2e2;
10292  color: #991b1b;
10293  border: 1px solid #fca5a5;
10294  margin-left: 8px;
10295  vertical-align: middle;
10296}
10297
10298</style>
10299            
10300</head>
10301<body>
10302<header class="topbar">
10303  <div class="container topbar-inner">
10304    <div class="topbar-left">
10305      EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap
10306    </div>
10307<div class="topbar-right">
10308  <a href="https://norita.mx/">EDUTOTO</a>
10309  <a href="https://norita.mx/">EDUTOTO LOGIN</a>
10310  <a href="https://norita.mx/">ALTERNATIF EDUTOTO</a>
10311  <a href="https://norita.mx/">WEBSITE EDUTOTO</a>
10312</div>
10313  </div>
10314</header>
10315<nav class="navbar">
10316  <nav class="navbar-logo" style="width: 100%;">
10317    <div class="container nav-inner" style="justify-items: center; justify-content: center;">
10318        <a href="https://norita.mx/" class="logo-mobile" title="EDUTOTO">
10319          <img src="/img/logo-web.png" alt="EDUTOTO" height="40">
10320        </a>
10321    </div>
10322</nav>
10323  <div class="container nav-inner">
10324    <a href="https://norita.mx/" class="logo" title="EDUTOTO">
10325      <img src="/img/logo-web.png" alt="EDUTOTO" height="40">
10326    </a>
10327
10328    
10329    <form class="search" method="get" action="#">
10330      <input type="hidden" name="page" value="produk">
10331      
10332      <!-- Search Icon (Desktop) -->
10333      <button type="submit" class="search-icon-btn" id="searchBtn" aria-label="Cari">
10334        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
10335          <circle cx="11" cy="11" r="8"/>
10336          <path d="m21 21-4.35-4.35"/>
10337        </svg>
10338      </button>
10339      
10340      <input type="text" name="q" id="searchInput" placeholder="Cari Aja Di Google EDUTOTO Bre !"
10341             value="" autocomplete="off"> 
10342      
10343      <div class="search-suggest" id="searchSuggest" hidden></div>
10344    </form>
10345
10346    <div class="nav-actions">
10347      <!--<button class="icon-btn" id="notifBtn" title="Notifikasi">🔔</button>-->
10348      <button class="icon-btn cart-indicator" id="cartBtn" title="Keranjang">
10349        🛒 <span id="cartCount" class="badge" data-role="cart-badge" aria-live="polite">0</span>
10350      </button>
10351
10352      <button class="icon-btn compare-indicator" id="compareBtn" title="Perbandingan">
10353        ⚖️ <span id="compareCount" class="badge" data-role="compare-badge" aria-live="polite">0</span>
10354      </button>
10355      
10356      <!-- Dynamic login/logout button -->
10357            <button class="login-btn" id="loginBtn" aria-label="Masuk">
10358        <span class="login-text">Masuk</span>
10359        <span class="login-ico" aria-hidden="true">😊</span>
10360      </button>
10361          </div>
10362  </div>
10363
10364  <div class="container" style="display: flex; align-items: center;">
10365      
10366
10367<div class="mega-cats-wrapper">
10368  <div class="mega-cats container" id="megaCatsScroll" style="padding-left: 5px !important;">
10369                <a class="" href="https://norita.mx/">
10370          EDUTOTO
10371        </a>
10372                <a class="" href="https://norita.mx/">
10373          EDUTOTO LOGIN
10374        </a>
10375                <a class="" href="https://norita.mx/">
10376          ALTERNATIF EDUTOTO
10377        </a>
10378                <a class="" href="https://norita.mx/">
10379          EDUTOTO DAFTAR
10380        </a>
10381                <a class="" href="https://norita.mx/">
10382          EDUTOTO SLOT
10383        </a>
10384                <a class="" href="https://norita.mx/">
10385          WEBSITE EDUTOTO
10386        </a>
10387            </div>
10388  <div class="mega-cats-fade" id="megaCatsFade"></div>
10389</div>
10390  </div>
10391</nav>
10392
10393<!-- Panel Mega Menu - Outside navbar for full-screen -->
10394<div class="mm2-panel" data-mm2="panel" role="dialog" aria-label="Kategori">
10395  <div class="mm2-view is-active" data-mm2="top"></div>
10396  <div class="mm2-view" data-mm2="detail"></div>
10397</div>
10398
10399<!-- Backdrop - Outside navbar for full-screen -->
10400<div class="mm2-backdrop" data-mm2="backdrop"></div>
10401
10402<!-- Mobile Bottom Navigation -->
10403<nav class="bottom-nav">
10404  <div class="bottom-nav-inner">
10405    <!-- Home -->
10406    <a href="/" class="bottom-nav-item" id="bottomHomeBtn" data-page="home">
10407      <div class="bottom-nav-item-icon">
10408        <!-- Outlined icon (default) -->
10409        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10410          <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
10411          <polyline points="9 22 9 12 15 12 15 22"/>
10412        </svg>
10413        <!-- Filled icon (active) -->
10414        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10415          <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"/>
10416          <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"/>
10417        </svg>
10418      </div>
10419      <span class="bottom-nav-item-label">Home</span>
10420    </a>
10421
10422    <!-- Kategori -->
10423    <button class="bottom-nav-item" id="bottomCategoryBtn" data-mm2="trigger" aria-label="Kategori">
10424      <div class="bottom-nav-item-icon">
10425        <!-- Outlined icon (default) -->
10426        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10427          <line x1="3" y1="12" x2="21" y2="12"/>
10428          <line x1="3" y1="6" x2="21" y2="6"/>
10429          <line x1="3" y1="18" x2="21" y2="18"/>
10430        </svg>
10431        <!-- Filled icon (active) -->
10432        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10433          <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"/>
10434        </svg>
10435      </div>
10436      <span class="bottom-nav-item-label">Kategori</span>
10437    </button>
10438
10439    <!-- Keranjang -->
10440    <button class="bottom-nav-item" id="bottomCartBtn">
10441      <div class="bottom-nav-item-icon">
10442        <!-- Outlined icon (default) -->
10443        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10444          <circle cx="9" cy="21" r="1"/>
10445          <circle cx="20" cy="21" r="1"/>
10446          <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>
10447        </svg>
10448        <!-- Filled icon (active) -->
10449        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10450          <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"/>
10451        </svg>
10452        <span class="bottom-nav-item-badge" id="bottomCartCount">0</span>
10453      </div>
10454      <span class="bottom-nav-item-label">Keranjang</span>
10455    </button>
10456
10457    <!-- Compare -->
10458    <button class="bottom-nav-item" id="bottomCompareBtn">
10459      <div class="bottom-nav-item-icon">
10460        <!-- Outlined icon (default) -->
10461        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10462          <polyline points="17 11 21 7 17 3"/>
10463          <path d="M21 7H9"/>
10464          <polyline points="7 21 3 17 7 13"/>
10465          <path d="M15 17H3"/>
10466        </svg>
10467        <!-- Filled icon (active) -->
10468        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10469          <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"/>
10470        </svg>
10471        <span class="bottom-nav-item-badge" id="bottomCompareCount" style="display:none;">0</span>
10472      </div>
10473      <span class="bottom-nav-item-label">Compare</span>
10474    </button>
10475
10476    <!-- Chat - Always show, check login in JavaScript -->
10477    <button class="bottom-nav-item" id="bottomChatBtn" data-page="chat">
10478      <div class="bottom-nav-item-icon">
10479        <!-- Outlined icon (default) -->
10480        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" opacity="0.85">
10481          <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
10482        </svg>
10483        <!-- Filled icon (active) -->
10484        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10485          <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/>
10486        </svg>
10487        <span class="bottom-nav-item-badge" id="bottomChatCount" style="display:none;">0</span>
10488      </div>
10489      <span class="bottom-nav-item-label">Chat</span>
10490    </button>
10491
10492    <!-- Akun -->
10493        <button class="bottom-nav-item" id="bottomLoginBtn">
10494      <div class="bottom-nav-item-icon">
10495        <!-- Outlined icon (default) -->
10496        <svg class="icon-outlined" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
10497          <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
10498          <circle cx="12" cy="7" r="4"/>
10499        </svg>
10500        <!-- Filled icon (active) -->
10501        <svg class="icon-filled" viewBox="0 0 24 24" fill="currentColor">
10502          <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"/>
10503        </svg>
10504      </div>
10505      <span class="bottom-nav-item-label">Login</span>
10506    </button>
10507      </div>
10508</nav>
10509
10510<!-- Mobile User Menu Bottom Sheet -->
10511
10512<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
10513(function(){
10514  const btn = document.getElementById('megaMoreBtn');
10515  const dd  = document.getElementById('megaMoreDropdown');
10516  if(!btn || !dd) return;
10517
10518  function openDD(){
10519    dd.hidden = false;
10520    btn.setAttribute('aria-expanded','true');
10521    document.addEventListener('click', outside, {capture:true});
10522    document.addEventListener('keydown', esc);
10523  }
10524  function closeDD(){
10525    if(dd.hidden) return;
10526    dd.hidden = true;
10527    btn.setAttribute('aria-expanded','false');
10528    document.removeEventListener('click', outside, {capture:true});
10529    document.removeEventListener('keydown', esc);
10530  }
10531  function toggle(){ dd.hidden ? openDD() : closeDD(); }
10532  function outside(e){
10533    if(!dd.contains(e.target) && e.target !== btn){
10534      closeDD();
10535    }
10536  }
10537  function esc(e){
10538    if(e.key === 'Escape'){
10539      closeDD();
10540      btn.focus();
10541    }
10542  }
10543  btn.addEventListener('click', toggle);
10544})();
10545
10546// ========== HORIZONTAL SCROLL FADE INDICATOR ==========
10547(function(){
10548  const megaCats = document.getElementById('megaCatsScroll');
10549  const fade = document.getElementById('megaCatsFade');
10550  if (!megaCats || !fade) return;
10551
10552  function updateFade() {
10553    const scrollLeft = megaCats.scrollLeft;
10554    const scrollWidth = megaCats.scrollWidth;
10555    const clientWidth = megaCats.clientWidth;
10556    const maxScroll = scrollWidth - clientWidth;
10557    
10558    // Hide fade when scrolled to end (within 10px tolerance)
10559    if (maxScroll - scrollLeft <= 10) {
10560      fade.style.opacity = '0';
10561    } else {
10562      fade.style.opacity = '1';
10563    }
10564  }
10565
10566  // Update on scroll
10567  megaCats.addEventListener('scroll', updateFade, { passive: true });
10568  
10569  // Update on resize
10570  window.addEventListener('resize', updateFade, { passive: true });
10571  
10572  // Initial check
10573  setTimeout(updateFade, 100);
10574})();
10575
10576document.addEventListener('DOMContentLoaded', function () {
10577  var input = document.getElementById('searchInput');
10578  if (!input) return;
10579  input.addEventListener('keydown', function (e) {
10580    if (e.key === 'Enter') {
10581      e.preventDefault();
10582      var form = input.form;
10583      if (!form) return;
10584      if (typeof form.requestSubmit === 'function') form.requestSubmit();
10585      else form.submit();
10586    }
10587  });
10588});
10589
10590
10591(function(){
10592  const cartBtn = document.getElementById('cartBtn');
10593  if (cartBtn) {
10594    cartBtn.addEventListener('click', function(e) {
10595      e.preventDefault();
10596      
10597      const checkLogin = () => {
10598        return window.isUserLoggedIn ? window.isUserLoggedIn() : false;
10599      };
10600      
10601      if (checkLogin()) {
10602        window.location.href = '/keranjang';
10603      } else {
10604        showCartLoginModal();
10605      }
10606    });
10607  }
10608
10609  const loginBtn = document.getElementById('loginBtn');
10610  if (loginBtn) {
10611    loginBtn.addEventListener('click', function(e) {
10612      e.preventDefault();
10613      window.location.href = 'https://norita.mx/';
10614    });
10615  }
10616
10617  const userMenuBtn = document.getElementById('userMenuBtn');
10618  const userDropdown = document.getElementById('userDropdown');
10619  
10620  if (userMenuBtn && userDropdown) {
10621    function openDropdown() {
10622      userDropdown.hidden = false;
10623      userMenuBtn.setAttribute('aria-expanded', 'true');
10624      document.addEventListener('click', closeOnOutside, { capture: true });
10625      document.addEventListener('keydown', closeOnEscape);
10626    }
10627    
10628    function closeDropdown() {
10629      if (userDropdown.hidden) return;
10630      userDropdown.hidden = true;
10631      userMenuBtn.setAttribute('aria-expanded', 'false');
10632      document.removeEventListener('click', closeOnOutside, { capture: true });
10633      document.removeEventListener('keydown', closeOnEscape);
10634    }
10635    
10636    function closeOnOutside(e) {
10637      if (!userDropdown.contains(e.target) && e.target !== userMenuBtn) {
10638        closeDropdown();
10639      }
10640    }
10641    
10642    function closeOnEscape(e) {
10643      if (e.key === 'Escape') {
10644        closeDropdown();
10645        userMenuBtn.focus();
10646      }
10647    }
10648    
10649    userMenuBtn.addEventListener('click', function(e) {
10650      e.preventDefault();
10651      userDropdown.hidden ? openDropdown() : closeDropdown();
10652    });
10653    
10654    // Close dropdown when clicking profile links
10655    userDropdown.querySelectorAll('[data-close-dropdown]').forEach(link => {
10656      link.addEventListener('click', function() {
10657        closeDropdown();
10658      });
10659    });
10660  }
10661
10662  // Bottom nav profile button - toggle mobile bottom sheet
10663  const bottomProfileBtn = document.getElementById('bottomProfileBtn');
10664  const mobileUserSheet = document.getElementById('mobileUserSheet');
10665  const mobileUserSheetBackdrop = document.getElementById('mobileUserSheetBackdrop');
10666  
10667  if (bottomProfileBtn && mobileUserSheet) {
10668    function openMobileSheet() {
10669      mobileUserSheet.hidden = false;
10670      document.body.style.overflow = 'hidden';
10671      // Trigger animation
10672      requestAnimationFrame(() => {
10673        mobileUserSheet.classList.add('active');
10674      });
10675    }
10676    
10677    function closeMobileSheet() {
10678      mobileUserSheet.classList.remove('active');
10679      document.body.style.overflow = '';
10680      setTimeout(() => {
10681        mobileUserSheet.hidden = true;
10682      }, 300);
10683    }
10684    
10685    bottomProfileBtn.addEventListener('click', function(e) {
10686      e.preventDefault();
10687      openMobileSheet();
10688    });
10689    
10690    if (mobileUserSheetBackdrop) {
10691      mobileUserSheetBackdrop.addEventListener('click', closeMobileSheet);
10692    }
10693    
10694    // Close on escape key
10695    document.addEventListener('keydown', function(e) {
10696      if (e.key === 'Escape' && !mobileUserSheet.hidden) {
10697        closeMobileSheet();
10698      }
10699    });
10700    
10701    // Close sheet when clicking profile links
10702    mobileUserSheet.querySelectorAll('[data-close-sheet]').forEach(link => {
10703      link.addEventListener('click', function() {
10704        closeMobileSheet();
10705      });
10706    });
10707  }
10708  
10709  // Handle Google avatar error in bottom-nav (fallback to initials)
10710  const bottomNavAvatar = document.querySelector('.bottom-nav-avatar-img');
10711  if (bottomNavAvatar) {
10712    bottomNavAvatar.addEventListener('error', function() {
10713      const initials = this.getAttribute('data-fallback-initials') || 'U';
10714      const initialsDiv = document.createElement('div');
10715      initialsDiv.className = 'bottom-nav-avatar-initials';
10716      initialsDiv.textContent = initials;
10717      this.parentNode.replaceChild(initialsDiv, this);
10718    });
10719  }
10720
10721  // Mobile logout button
10722  const mobileLogoutBtn = document.getElementById('mobileLogoutBtn');
10723  if (mobileLogoutBtn) {
10724    mobileLogoutBtn.addEventListener('click', async function(e) {
10725      e.preventDefault();
10726      
10727      // Check push notification before logout
10728      if (window.ChatNotification && typeof window.ChatNotification.confirmLogoutWithPushNotification === 'function') {
10729        const confirmLogout = await window.ChatNotification.confirmLogoutWithPushNotification();
10730        if (!confirmLogout) {
10731          return; // User cancelled logout
10732        }
10733      }
10734      
10735      const getCsrfToken = () => document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
10736      
10737      try {
10738        const response = await fetch('/auth/logout', {
10739          method: 'POST',
10740          credentials: 'same-origin',
10741          headers: {
10742            'Content-Type': 'application/json',
10743            'X-CSRF-Token': getCsrfToken()
10744          },
10745          body: JSON.stringify({
10746            csrf_token: getCsrfToken()
10747          })
10748        });
10749        
10750        if (response.ok) {
10751          if (window.guestCart && typeof window.guestCart.clear === 'function') {
10752            window.guestCart.clear();
10753          }
10754          window.location.href = '/login?logged_out=1';
10755        } else {
10756          console.error('Logout failed');
10757          window.location.href = '/logout';
10758        }
10759      } catch (error) {
10760        console.error('Logout error:', error);
10761        window.location.href = '/logout';
10762      }
10763    });
10764  }
10765
10766  const logoutBtn = document.getElementById('logoutBtn');
10767  if (logoutBtn) {
10768    logoutBtn.addEventListener('click', async function(e) {
10769      e.preventDefault();
10770      
10771      // Check push notification before logout
10772      if (window.ChatNotification && typeof window.ChatNotification.confirmLogoutWithPushNotification === 'function') {
10773        const confirmLogout = await window.ChatNotification.confirmLogoutWithPushNotification();
10774        if (!confirmLogout) {
10775          return; // User cancelled logout
10776        }
10777      }
10778      
10779      const getCsrfToken = () => document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
10780      
10781      try {
10782        const response = await fetch('/auth/logout', {
10783          method: 'POST',
10784          credentials: 'same-origin',
10785          headers: {
10786            'Content-Type': 'application/json',
10787            'X-CSRF-Token': getCsrfToken()
10788          },
10789          body: JSON.stringify({
10790            csrf_token: getCsrfToken()
10791          })
10792        });
10793        
10794        if (response.ok) {
10795          if (window.guestCart && typeof window.guestCart.clear === 'function') {
10796            window.guestCart.clear();
10797          }
10798          
10799          window.location.href = '/login?logged_out=1';
10800        } else {
10801          console.error('Logout failed');
10802          window.location.href = '/logout';
10803        }
10804      } catch (error) {
10805        console.error('Logout error:', error);
10806        window.location.href = '/logout';
10807      }
10808    });
10809  }
10810
10811  const wishlistBtn = document.getElementById('wishlistBtn');
10812  const notifBtn = document.getElementById('notifBtn');
10813  
10814  if (wishlistBtn) {
10815    wishlistBtn.addEventListener('click', function(e) {
10816      e.preventDefault();
10817      alert('Fitur wishlist akan segera tersedia!');
10818    });
10819  }
10820  
10821  if (notifBtn) {
10822    notifBtn.addEventListener('click', function(e) {
10823      e.preventDefault();
10824      alert('Fitur notifikasi akan segera tersedia!');
10825    });
10826  }
10827})();
10828
10829// ========== MOBILE BOTTOM NAVIGATION ==========
10830(function initBottomNav() {
10831  // Sync cart badge with bottom nav
10832  function syncBottomCartBadge() {
10833    const mainBadge = document.getElementById('cartCount');
10834    const bottomBadge = document.getElementById('bottomCartCount');
10835    if (mainBadge && bottomBadge) {
10836      const count = mainBadge.textContent;
10837      bottomBadge.textContent = count;
10838      bottomBadge.style.display = parseInt(count) > 0 ? 'flex' : 'none';
10839    }
10840  }
10841
10842  // Sync compare badge with bottom nav
10843  function syncBottomCompareBadge() {
10844    const mainBadge = document.getElementById('compareCount');
10845    const bottomBadge = document.getElementById('bottomCompareCount');
10846    if (mainBadge && bottomBadge) {
10847      const count = mainBadge.textContent;
10848      bottomBadge.textContent = count;
10849      bottomBadge.style.display = parseInt(count) > 0 ? 'flex' : 'none';
10850    }
10851  }
10852
10853  // Initial sync
10854  syncBottomCartBadge();
10855  syncBottomCompareBadge();
10856
10857  // Observe badge changes
10858  const observer = new MutationObserver(function() {
10859    syncBottomCartBadge();
10860    syncBottomCompareBadge();
10861  });
10862
10863  const cartBadge = document.getElementById('cartCount');
10864  const compareBadge = document.getElementById('compareCount');
10865  
10866  if (cartBadge) observer.observe(cartBadge, { childList: true, characterData: true, subtree: true });
10867  if (compareBadge) observer.observe(compareBadge, { childList: true, characterData: true, subtree: true });
10868
10869  // Bottom Cart Button
10870  const bottomCartBtn = document.getElementById('bottomCartBtn');
10871  if (bottomCartBtn) {
10872    bottomCartBtn.addEventListener('click', function(e) {
10873      e.preventDefault();
10874      
10875      const checkLogin = () => {
10876        return window.isUserLoggedIn ? window.isUserLoggedIn() : false;
10877      };
10878      
10879      if (checkLogin()) {
10880        window.location.href = '/keranjang';
10881      } else {
10882        showCartLoginModal();
10883      }
10884    });
10885  }
10886
10887  // Bottom Compare Button
10888  const bottomCompareBtn = document.getElementById('bottomCompareBtn');
10889  if (bottomCompareBtn) {
10890    bottomCompareBtn.addEventListener('click', function(e) {
10891      e.preventDefault();
10892      
10893      // Close chat widget if open
10894      if (window.plazaChat && window.plazaChat.isWidgetOpen) {
10895        window.plazaChat.closeWidget();
10896      }
10897      
10898      const mainCompareBtn = document.getElementById('compareBtn');
10899      if (mainCompareBtn) {
10900        mainCompareBtn.click();
10901      }
10902    });
10903  }
10904
10905  // Bottom Category Button - triggers mega menu
10906  const bottomCategoryBtn = document.getElementById('bottomCategoryBtn');
10907  if (bottomCategoryBtn) {
10908    bottomCategoryBtn.addEventListener('click', function(e) {
10909      e.preventDefault();
10910      // Trigger desktop mega menu
10911      const desktopTrigger = document.querySelector('.mm2-trigger:not(#bottomCategoryBtn)');
10912      if (desktopTrigger) {
10913        desktopTrigger.click();
10914      }
10915    });
10916  }
10917
10918  // Bottom Login Button
10919  const bottomLoginBtn = document.getElementById('bottomLoginBtn');
10920  if (bottomLoginBtn) {
10921    bottomLoginBtn.addEventListener('click', function(e) {
10922      e.preventDefault();
10923      window.location.href = '/login';
10924    });
10925  }
10926
10927  // Bottom Chat Button
10928  const bottomChatBtn = document.getElementById('bottomChatBtn');
10929  if (bottomChatBtn) {
10930    bottomChatBtn.addEventListener('click', function(e) {
10931      e.preventDefault();
10932      
10933      // Trigger chat widget (try multiple times if needed)
10934      const triggerChat = () => {
10935        const chatToggle = document.getElementById('chat-toggle-btn');
10936        if (chatToggle) {
10937          chatToggle.click();
10938          return true;
10939        }
10940        return false;
10941      };
10942      
10943      // Immediate attempt
10944      if (!triggerChat()) {
10945        // Retry after 100ms if not found
10946        setTimeout(() => {
10947          if (!triggerChat()) {
10948            // Last resort: manually show popup
10949            const popup = document.getElementById('chat-popup');
10950            if (popup) {
10951              popup.style.display = 'block';
10952            }
10953          }
10954        }, 100);
10955      }
10956    });
10957  }
10958
10959  // Sync chat unread badge
10960  function syncBottomChatBadge() {
10961    const chatBadge = document.getElementById('chat-unread-badge');
10962    const bottomChatBadge = document.getElementById('bottomChatCount');
10963    if (chatBadge && bottomChatBadge) {
10964      const count = chatBadge.textContent;
10965      bottomChatBadge.textContent = count;
10966      bottomChatBadge.style.display = chatBadge.style.display;
10967    }
10968  }
10969
10970  // Observe chat badge changes
10971  const chatBadge = document.getElementById('chat-unread-badge');
10972  if (chatBadge) {
10973    observer.observe(chatBadge, { attributes: true, childList: true, characterData: true, subtree: true });
10974    syncBottomChatBadge();
10975  }
10976
10977  // Active state management based on current page
10978  function updateActiveState() {
10979    const currentPath = window.location.pathname;
10980    const bottomNavItems = document.querySelectorAll('.bottom-nav-item');
10981    
10982    bottomNavItems.forEach(item => {
10983      item.classList.remove('active');
10984      
10985      const itemId = item.id;
10986      const page = item.getAttribute('data-page');
10987      const href = item.getAttribute('href');
10988      
10989      // Check by ID for specific pages
10990      if (itemId === 'bottomHomeBtn' && currentPath === '/') {
10991        item.classList.add('active');
10992      } else if (itemId === 'bottomCartBtn' && (currentPath === '/keranjang' || currentPath === '/cart')) {
10993        item.classList.add('active');
10994      } else if (itemId === 'bottomProfileBtn' && currentPath.startsWith('/profile')) {
10995        item.classList.add('active');
10996      } else if (itemId === 'bottomCategoryBtn' && currentPath.startsWith('/kategori')) {
10997        item.classList.add('active');
10998      } else if (itemId === 'bottomCompareBtn' && currentPath === '/compare') {
10999        item.classList.add('active');
11000      } else if (itemId === 'bottomChatBtn' && currentPath === '/chat') {
11001        item.classList.add('active');
11002      }
11003      // Fallback: check by data-page attribute
11004      else if (page === 'home' && currentPath === '/') {
11005        item.classList.add('active');
11006      } else if (page === 'profile' && currentPath.startsWith('/profile')) {
11007        item.classList.add('active');
11008      }
11009      // Fallback: check by href
11010      else if (href && currentPath === href) {
11011        item.classList.add('active');
11012      }
11013    });
11014  }
11015
11016  // Update active state on load
11017  updateActiveState();
11018
11019  // Bottom nav always visible - auto-hide disabled for better UX
11020  // const bottomNav = document.querySelector('.bottom-nav');
11021  // if (bottomNav && window.innerWidth <= 640) {
11022  //   // Auto-hide feature disabled
11023  // }
11024})();
11025// Cart Login Modal Handler (Global scope)
11026function showCartLoginModal() {
11027  const modal = document.getElementById('cartLoginModal');
11028  if (!modal) return;
11029  
11030  modal.classList.add('show');
11031  
11032  const confirmBtn = document.getElementById('cartLoginConfirm');
11033  const registerBtn = document.getElementById('cartRegisterBtn');
11034  const cancelBtn = document.getElementById('cartLoginCancel');
11035  
11036  const handleLogin = () => {
11037    window.location.href = '/login?return=' + encodeURIComponent('/keranjang');
11038  };
11039  
11040  const handleRegister = () => {
11041    window.location.href = '/register?return=' + encodeURIComponent('/keranjang');
11042  };
11043  
11044  const handleCancel = () => {
11045    modal.classList.remove('show');
11046    confirmBtn.removeEventListener('click', handleLogin);
11047    registerBtn.removeEventListener('click', handleRegister);
11048    cancelBtn.removeEventListener('click', handleCancel);
11049  };
11050  
11051  confirmBtn.removeEventListener('click', handleLogin);
11052  registerBtn.removeEventListener('click', handleRegister);
11053  cancelBtn.removeEventListener('click', handleCancel);
11054  
11055  confirmBtn.addEventListener('click', handleLogin);
11056  registerBtn.addEventListener('click', handleRegister);
11057  cancelBtn.addEventListener('click', handleCancel);
11058  
11059  modal.addEventListener('click', (e) => {
11060    if (e.target === modal) {
11061      handleCancel();
11062    }
11063  });
11064}
11065</script>
vendor: 2 bytes, line 11065
11065
11066<script data-inlined="cart-badge.js">
11067(function(){
11068  function selBadge(){
11069    return document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
11070  }
11071  function setBadge(n){
11072    const el = selBadge();
11073    if (!el) return;
11074    const count = Math.max(0, parseInt(n||0,10) || 0);
11075    el.textContent = String(count);
11076    // Sembunyikan jika 0? Jika ingin selalu terlihat, hapus 2 baris di bawah
11077    if (count <= 0) { el.classList.add('is-empty'); }
11078    else { el.classList.remove('is-empty'); }
11079  }
11080  async function refresh(){
11081    try{
11082      // Primary: Use server-side endpoint (handles both logged and guest users)
11083      const res = await fetch('/api/cart/get-simple.php', { credentials:'same-origin' });
11084      if (res.ok) {
11085        const data = await res.json();
11086        if (data && data.ok) {
11087          setBadge(data.items_count || 0);
11088          return;
11089        }
11090      }
11091      
11092      // Fallback hanya untuk guest user: check localStorage
11093      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
11094                        document.body.classList.contains('logged-in') ||
11095                        window.userLoggedIn === true;
11096      
11097      if (!isLoggedIn && window.guestCart && typeof window.guestCart.getItemsCount === 'function') {
11098        setBadge(window.guestCart.getItemsCount());
11099      }
11100    }catch(e){ 
11101      // Final fallback hanya untuk guest
11102      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true';
11103      if (!isLoggedIn) {
11104        try {
11105          const guestCartData = JSON.parse(localStorage.getItem('plazait_guest_cart') || '{"items":{}}');
11106          const items = guestCartData.items || {};
11107          const count = Object.values(items).reduce((total, item) => total + (item.qty || 0), 0);
11108          setBadge(count);
11109        } catch(e2) {
11110          setBadge(0);
11111        }
11112      } else {
11113        setBadge(0);
11114      }
11115    }
11116  }
11117
11118  // Ekspos untuk dipanggil dari skrip lain (opsional)
11119  window.CartBadge = { set: setBadge, refresh };
11120  window.updateCartCounter = refresh; // Alias for compatibility
11121
11122  // Update saat halaman load
11123  if (document.readyState === 'loading') {
11124    document.addEventListener('DOMContentLoaded', refresh);
11125  } else {
11126    refresh();
11127  }
11128
11129  // Dengarkan event global dari add/remove/update
11130  document.addEventListener('cart:updated', function(ev){
11131    const c = ev && ev.detail && typeof ev.detail.items_count === 'number'
11132      ? ev.detail.items_count : null;
11133    if (c !== null) setBadge(c);
11134    else refresh();
11135  });
11136
11137  // Dengarkan event khusus cart remove untuk update badge
11138  document.addEventListener('cart:removed', function(ev){
11139    // Langsung refresh untuk memastikan data terbaru
11140    refresh();
11141  });
11142
11143  // Jika addToCart sudah ada sebelum file ini, coba wrap agar badge ter-update.
11144  const prevAdd = window.addToCart;
11145  if (typeof prevAdd === 'function'){
11146    window.addToCart = async function(pid, qty, vid, meta){
11147      const res = await prevAdd(pid, qty, vid, meta);
11148      try{
11149        const cnt = res && res.cart ? res.cart.items_count : null;
11150        if (cnt !== null) setBadge(cnt);
11151        // tetap siarkan event agar listener lain ikut sync
11152        document.dispatchEvent(new CustomEvent('cart:updated', { detail: res.cart || {} }));
11153      }catch(_){}
11154      return res;
11155    };
11156  }
11157
11158  // Helper function to check if user is logged in
11159  function isUserLoggedIn() {
11160    const metaTag = document.querySelector('meta[name="user-logged-in"]');
11161    return metaTag ? metaTag.getAttribute('content') === 'true' : false;
11162  }
11163
11164  // Make login check globally accessible
11165  window.isUserLoggedIn = isUserLoggedIn;
11166})();
11167</script>
11167
11168
11169<!-- Product Compare Sidebar -->
11170<div class="compare-sidebar" id="compareSidebar">
11171  <div class="compare-header">
11172    <h4 class="compare-title">
11173      <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
11174        <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"/>
11175      </svg>
11176      Bandingkan Produk
11177      <span class="compare-count" id="compareCountSidebar">(0)</span>
11178    </h4>
11179    <button class="compare-close" id="compareClose" aria-label="Tutup">
11180      <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
11181        <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"/>
11182      </svg>
11183    </button>
11184  </div>
11185  
11186  <div class="compare-content">
11187    <div class="compare-empty" id="compareEmpty">
11188      <div class="empty-icon">
11189        <svg width="48" height="48" viewBox="0 0 24 24" fill="currentColor" opacity="0.3">
11190          <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"/>
11191        </svg>
11192      </div>
11193      <h5>Belum ada produk dipilih</h5>
11194      <p>Pilih produk yang ingin dibandingkan dengan klik tombol "Bandingkan Produk" di halaman produk.</p>
11195    </div>
11196    
11197    <div class="compare-list" id="compareList">
11198      <!-- Products will be populated here -->
11199    </div>
11200  </div>
11201  
11202  <div class="compare-actions" id="compareActions">
11203    <button class="btn btn-outline-danger btn-sm" id="compareClearAll">
11204      <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
11205        <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"/>
11206      </svg>
11207      Hapus Semua
11208    </button>
11209    <button class="btn btn-primary btn-sm" id="compareAnalyze" disabled>
11210      <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
11211        <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"/>
11212      </svg>
11213      Analisis AI
11214    </button>
11215  </div>
11216  
11217  <!-- AI Disclaimer in Sidebar -->
11218  <div class="compare-disclaimer">
11219    <div class="ai-disclaimer">
11220<div class="disclaimer-text">
11221  <strong>💡 Catatan:</strong> EDUTOTO menghadirkan sensasi relaksasi premium dengan akses nyaman dan fitur lengkap. Informasi di halaman ini membantu Anda memahami kenyamanan platform, alur penggunaan, serta pengalaman bermain yang lebih santai.
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(255,255,255,.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,#EAB308 0,#8B6914 100%);border:none;border-radius:8px;padding:3px;cursor: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(255,255,255,.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:#EAB308;cursor:pointer;transition:all .2s ease;white-space:nowrap}.pdv-min-copy:hover:not(:disabled),.pdv-min-more:hover{background:#EAB308;color:#fff;border-color:#EAB308}.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,#EAB308 0,#8B6914 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:#EAB308;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:#EAB308;flex-shrink:0;margin-top:2px}.vmd-item strong{color:#1e293b}.modal-actions{padding:20px}.modal-btn-primary{background:linear-gradient(135deg,#EAB308 0,#8B6914 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(255,255,255,.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:2px solid rgba(201,162,39,.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(201,162,39,.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:#C9A227;margin-bottom:2px}
11288.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,#C9A227,#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(201,162,39,.25)}.pey-link:hover{transform:translateX(2px);box-shadow:0 4px 12px rgba(201,162,39,.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;display: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:#EAB308;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,#FCD34D 0%,#FBBF24 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:#EAB308;box-shadow:0 0 0 3px rgba(201,162,39,.1)}.pd-share-copy-btn{padding:12px 20px;border:0;border-radius:10px;background:linear-gradient(135deg,#EAB308 0%,#A16207 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,#A16207 0%,#92400E 100%);transform:translateY(-1px);box-shadow:0 4px 12px rgba(201,162,39,.3)}.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:#FBBF24;border:2px solid #FBBF24;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:#EAB308;color:#14b1d7;border-radius:999px;padding:4px 10px;font-weight:700}
11311
11312.stock-info-btn{--c:#EAB308;--b:#FDE68A;--bg:#FFFBEB;--bg-h:#FEF9C3;--b-h:#EAB308;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:#A16207;transform:translateY(-1px);box-shadow:0 2px 8px rgba(201,162,39,.15)}.stock-info-btn:active{transform:translateY(0)}.stock-info-btn:focus-visible{outline:0;box-shadow:0 0 0 3px rgba(201,162,39,.2)}.stock-info-btn svg{width:14px;
11312height: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:#0b1220;color:#eef3ff;border:1px solid rgba(255,255,255,.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:#0b1220;border-left:1px solid rgba(255,255,255,.08);border-top:1px solid rgba(255,255,255,.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(255,255,255,.03)}.sp-left{display:flex;align-items:center;gap:10px}.sp-dot{width:10px;height:10px;border-radius:999px;background:var(--dot,#FBBF24);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:#CA8A04}.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,#EAB308 0%,#CA8A04 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 #EAB308}.pwp-deals-header{display:flex;gap:10px;margin-bottom:10px}.pwp-icon{flex-shrink:0;color:#CA8A04;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:#8B6914;opacity:0.9}.pwp-deals-loading{text-align:center;padding:40px 0;color:#8B6914}.pwp-spinner{width:40px;height:40px;border:3px solid #EAB308;border-top-color:#CA8A04;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 #EAB308;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:#FEF9C3;border-left:2px solid#EAB308;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,#CA8A04 0%,#8B6914 100%);color:#fff;border-radius:8px;font-size:13px;font-weight:700;border:0;box-shadow:0 2px 8px rgba(234,179,8,.25)}.preorder-badge svg{width:16px;height:16px;flex-shrink:0}.preorder-info{margin:12px 0;padding:14px;background:linear-gradient(135deg,#FFFBEB 0%,#FEF3C7 100%);border:1.5px solid #FDE68A;border-radius:12px}.preorder-info-header{display:flex;align-items:center;gap:8px;margin-bottom:10px}.preorder-info-icon{width:20px;height:20px;color:#8B6914;flex-shrink:0}.preorder-info-title{font-size:14px;font-weight:700;color:#78350F;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:#8B6914;font-weight:600}.preorder-info-value{color:#78350F;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 #CA8A04;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="img/banner-edutoto.webp" alt="EDUTOTO" id="pd-sticky-thumb-img"></div><div class="pd-sticky-info"><h2 class="pd-sticky-title" id="pd-sticky-title">EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap</h2><div class="pd-sticky-price-row"><span class="pd-sticky-price" id="pd-sticky-price">Rp 10.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">
11352<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://norita.mx/">EDUTOTO</a><span>-</span><a href="https://norita.mx/">EDUTOTO LOGIN</a><span>-</span><a href="https://norita.mx/">ALTERNATIF EDUTOTO</a><span>-</span><a href="https://norita.mx/">EDUTOTO DAFTAR</a><span>-</span><a href="https://norita.mx/">WEBSITE EDUTOTO</a><span>-</span><span class="current">EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap
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="img/banner-edutoto.webp" alt="SITUS SLOT" 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="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="eager"></div><div class="pd-mslide"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="lazy"></div><div class="pd-mslide"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="lazy"></div><div class="pd-mslide"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="lazy"></div><div class="pd-mslide"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="lazy"></div><div class="pd-mslide"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" 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="img/banner-edutoto.webp" data-alt="EDUTOTO LOGIN" data-slug="" aria-label="Gambar 1"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="img/banner-edutoto.webp" data-alt="EDUTOTO LOGIN" data-slug="img/banner-edutoto.webp" aria-label="Gambar 2"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="img/banner-edutoto.webp" data-alt="EDUTOTO LOGIN" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-223.webp" aria-label="Gambar 3"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="img/banner-edutoto.webp" data-alt="EDUTOTO LOGIN" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-776.webp" aria-label="Gambar 4"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="img/banner-edutoto.webp" data-alt
11363="EDUTOTO LOGIN" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-634.webp" aria-label="Gambar 5"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" loading="lazy"></button><button type="button" class="pd-thumb-btn " data-full="img/banner-edutoto.webp" data-alt="EDUTOTO LOGIN" data-slug="lenovo-legion-go-8asp2-83n0000rid-ryzen-z2-extreme-amd-radeon-graphics-eclipse-b-1761796941-892.webp" aria-label="Gambar 6"><img src="img/banner-edutoto.webp" alt="EDUTOTO LOGIN" 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">EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap</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 10.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">EDUTOTO</span>
11379                                                        <span class="sku">SKU: <span id="pd-sku">EDUTOTO</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>SLOT</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"><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:#FBBF24;"></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">
11389<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:#CA8A04;"></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-8as
11424p2-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 EDUTOTO:</strong></p><ul><li>Minimal Deposit: Rp.10.000,-</li><li>Jenis Permainan: EDUTOTO LOGIN</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: 328.328.328</li><li>Rating: ⭐⭐⭐⭐⭐</li><li>Daftar Buruan: <a href="https://relaksasi.norita.mx/">Klik Disini</a></li></ul></div><div class="EDUTOTO-container-v1 mini">
11449<div class="EDUTOTO-container">
11450
11451  <!-- HEADER -->
11452<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;800&family=Rajdhani:wght@500;700&display=swap" rel="stylesheet">
11453
11454<div class="rtp-widget">
11455
11456    <div class="rtp-header">
11457
11458        <div class="title">
11459
11460            <span class="live-text">LIVE RTP MONITOR</span>
11461
11462            <h1>LIVE RTP EDUTOTO</h1>
11463
11464        </div>
11465
11466        <div class="update-box">
11467
11468            <div class="update-item">
11469
11470                <span>Update terakhir</span>
11471
11472                <strong id="lastUpdate">--:-- WIB</strong>
11473
11474            </div>
11475
11476            <div class="update-item">
11477
11478                <span>Refresh berikutnya</span>
11479
11480                <strong id="nextUpdate">60 detik</strong>
11481
11482            </div>
11483
11484        </div>
11485
11486    </div>
11487
11488    <div class="cards">
11489
11490        <div class="card" data-index="0">
11491
11492            <div class="card-header">
11493
11494                <h3>PRAGMATIC PLAY</h3>
11495
11496                <span>LIVE</span>
11497
11498            </div>
11499
11500            <div class="rtp">
11501
11502                <strong class="value">0.00</strong>
11503
11504                <small>%</small>
11505
11506            </div>
11507
11508            <div class="bar">
11509
11510                <div class="fill"></div>
11511
11512            </div>
11513
11514            <div class="footer">
11515
11516                <span class="status">NORMAL</span>
11517
11518                <span class="trend">+0.00</span>
11519
11520            </div>
11521
11522        </div>
11523
11524        <div class="card" data-index="1">
11525
11526            <div class="card-header">
11527
11528                <h3>PG SOFT</h3>
11529
11530                <span>LIVE</span>
11531
11532            </div>
11533
11534            <div class="rtp">
11535
11536                <strong class="value">0.00</strong>
11537
11538                <small>%</small>
11539
11540            </div>
11541
11542            <div class="bar">
11543
11544                <div class="fill"></div>
11545
11546            </div>
11547
11548            <div class="footer">
11549
11550                <span class="status">NORMAL</span>
11551
11552                <span class="trend">+0.00</span>
11553
11554            </div>
11555
11556        </div>
11557
11558        <div class="card" data-index="2">
11559
11560            <div class="card-header">
11561
11562                <h3>JOKER123</h3>
11563
11564                <span>LIVE</span>
11565
11566            </div>
11567
11568            <div class="rtp">
11569
11570                <strong class="value">0.00</strong>
11571
11572                <small>%</small>
11573
11574            </div>
11575
11576            <div class="bar">
11577
11578                <div class="fill"></div>
11579
11580            </div>
11581
11582            <div class="footer">
11583
11584                <span class="status">NORMAL</span>
11585
11586                <span class="trend">+0.00</span>
11587
11588            </div>
11589
11590        </div>
11591
11592        <div class="card" data-index="3">
11593
11594            <div class="card-header">
11595
11596                <h3>MICROGAMING</h3>
11597
11598                <span>LIVE</span>
11599
11600            </div>
11601
11602            <div class="rtp">
11603
11604                <strong class="value">0.00</strong>
11605
11606                <small>%</small>
11607
11608            </div>
11609
11610            <div class="bar">
11611
11612                <div class="fill"></div>
11613
11614            </div>
11615
11616            <div class="footer">
11617
11618                <span class="status">NORMAL</span>
11619
11620                <span class="trend">+0.00</span>
11621
11622            </div>
11623
11624        </div>
11625
11626    </div>
11627
11628</div>
11629
11630                <style>
11631@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;800&family=Rajdhani:wght@500;700&display=swap');
11632
11633*{
11634    margin:0;
11635    padding:0;
11636    box-sizing:border-box;
11637}
11638
11639.rtp-widget{
11640    width:100%;
11641    max-width:430px;
11642    margin:20px auto;
11643    padding:18px;
11644    border-radius:24px;
11645    background:linear-gradient(180deg,#14131d,#0f1018);
11646    border:1px solid rgba(201,162,39,.28);
11647    box-shadow:
11648        0 0 35px rgba(201,162,39,.28),
11649        inset 0 1px rgba(255,255,255,.05);
11650    overflow:hidden;
11651}
11652
11653.rtp-header{
11654    margin-bottom:18px;
11655}
11656
11657.live-text{
11658    display:block;
11659    color:#FBBF24;
11660    font-size:12px;
11661    font-weight:700;
11662    letter-spacing:3px;
11663    margin-bottom:10px;
11664}
11665
11666.title h1{
11667    font-family:'Orbitron',sans-serif;
11668    font-size:27px;
11669    line-height:1.05;
11670    color:#fff;
11671    margin-bottom:18px;
11672}
11673
11674.update-box{
11675    display:flex;
11676    justify-content:space-between;
11677    gap:10px;
11678    padding:12px;
11679    background:rgba(255,255,255,.05);
11680    border:1px solid rgba(255,255,255,.05);
11681    border-radius:14px;
11682}
11683
11684.update-item{
11685    display:flex;
11686    flex-direction:column;
11687}
11688
11689.update-item span{
11690    color:#bdbdbd;
11691    font-size:11px;
11692}
11693
11694.update-item strong{
11695    margin-top:4px;
11696    color:#fff;
11697    font-family:'Orbitron';
11698    font-size:13px;
11699}
11700
11701.cards{
11702    display:grid;
11703    grid-template-columns:repeat(2,1fr);
11704    gap:14px;
11705}
11706
11707.card{
11708    position:relative;
11709    padding:14px;
11710    border-radius:16px;
11711    background:linear-gradient(180deg,#222836,#1a202b);
11712    border:1px solid rgba(255,255,255,.08);
11713    transition:.3s;
11714}
11715
11716.card:hover{
11717    transform:translateY(-4px);
11718    box-shadow:0 12px 25px rgba(0,0,0,.35);
11719}
11720
11721.card:nth-child(1),
11722.card:nth-child(2),
11723.card:nth-child(3),
11724.card:nth-child(4){
11725    border-top:3px solid #C9A227;
11726}
11727
11728.card-header{
11729    display:flex;
11730    justify-content:space-between;
11731    align-items:flex-start;
11732    margin-bottom:18px;
11733}
11734
11735.card-header h3{
11736    width:70%;
11737    font-size:12px;
11738    line-height:1.2;
11739    color:#fff;
11740    font-family:'Orbitron';
11741}
11742
11743.card-header span{
11744    padding:4px 8px;
11745    border-radius:20px;
11746    background:#12161f;
11747    color:#FBBF24;
11748    font-size:9px;
11749    font-weight:bold;
11750}
11751
11752.rtp{
11753    display:flex;
11754    align-items:flex-end;
11755    gap:4px;
11756    margin-bottom:14px;
11757}
11758
11759.value{
11760    font-family:'Orbitron';
11761    font-size:38px;
11762    line-height:1;
11763}
11764
11765.rtp small{
11766    color:#FBBF24;
11767    font-size:18px;
11768    font-family:'Orbitron';
11769    margin-bottom:3px;
11770}
11771
11772.bar{
11773    width:100%;
11774    height:8px;
11775    background:#434857;
11776    border-radius:20px;
11777    overflow:hidden;
11778}
11779
11780.fill{
11781    width:0%;
11782    height:100%;
11783    border-radius:20px;
11784    background:linear-gradient(90deg,#8B6914,#C9A227,#FBBF24,#FDE68A);
11785    transition:.8s;
11786}
11787
11788.footer{
11789    margin-top:14px;
11790    display:flex;
11791    justify-content:space-between;
11792    align-items:center;
11793}
11794
11795.status{
11796    font-size:12px;
11797    color:#FBBF24;
11798    font-weight:700;
11799}
11800
11801.trend{
11802    font-size:12px;
11803    color:#fff;
11804    font-weight:700;
11805}
11806
11807@media(max-width:420px){
11808
11809.rtp-widget{
11810    padding:15px;
11811}
11812
11813.title h1{
11814    font-size:24px;
11815}
11816
11817.value{
11818    font-size:32px;
11819}
11820
11821.card-header h3{
11822    font-size:11px;
11823}
11824
11825}
11826                </style>
11827
11828<script>
11829const providers = [
11830    { name: "PRAGMATIC PLAY", min: 86, max: 98 },
11831    { name: "PG SOFT", min: 84, max: 97 },
11832    { name: "JOKER123", min: 82, max: 96 },
11833    { name: "MICROGAMING", min: 83, max: 97 }
11834];
11835
11836const cards = document.querySelectorAll(".card");
11837const lastUpdate = document.getElementById("lastUpdate");
11838const nextUpdate = document.getElementById("nextUpdate");
11839
11840let countdown = 60;
11841
11842function seededRandom(seed){
11843    const x = Math.sin(seed) * 10000;
11844    return x - Math.floor(x);
11845}
11846
11847function statusRtp(value){
11848
11849    if(value >= 95){
11850        return {
11851            text:"PANAS",
11852            color:"#FDE68A"
11853        };
11854    }
11855
11856    if(value >= 89){
11857        return {
11858            text:"NORMAL",
11859            color:"#FBBF24"
11860        };
11861    }
11862
11863    return{
11864        text:"TURUN",
11865        color:"#CA8A04"
11866    };
11867
11868}
11869
11870function updateCards(){
11871
11872    const now = new Date();
11873
11874    lastUpdate.innerHTML = now.toLocaleTimeString("id-ID",{
11875        hour:"2-digit",
11876        minute:"2-digit"
11877    })+" WIB";
11878
11879    cards.forEach((card,index)=>{
11880
11881        const p = providers[index];
11882
11883        const rand = seededRandom(Date.now()/60000+(index*15));
11884
11885        const value = (
11886            p.min + rand * (p.max-p.min)
11887        );
11888
11889        const percent = value.toFixed(2);
11890
11891        card.querySelector(".value").innerHTML = percent;
11892
11893        card.querySelector(".fill").style.width = percent+"%";
11894
11895        const trend = (Math.random()*2-1).toFixed(2);
11896
11897        const trendEl = card.querySelector(".trend");
11898
11899        trendEl.innerHTML =
11900            (trend>=0?"+":"")+trend;
11901
11902        trendEl.style.color =
11903            trend>=0
11904            ? "#FCD34D"
11905            : "#A16207";
11906
11907        const st = statusRtp(value);
11908
11909        const status = card.querySelector(".status");
11910
11911        status.innerHTML = st.text;
11912
11913        status.style.color = st.color;
11914
11915    });
11916
11917}
11918
11919updateCards();
11920
11921setInterval(()=>{
11922
11923    countdown--;
11924
11925    nextUpdate.innerHTML =
11926        countdown+" detik";
11927
11928    if(countdown<=0){
11929
11930        countdown=60;
11931
11932        updateCards();
11933
11934    }
11935
11936},1000);
11937
11938</script>
11938
11939
11940<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: 2 bytes, line 11940
11940
11941<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: 2 bytes, line 11941
11941
11942<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: 2 bytes, line 11942
11942
11943<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: 2 bytes, line 11943
11943
11944<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: 2 bytes, line 11944
11944
11945<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: 2 bytes, line 11945
11945
11946<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>
11946
11947
11948<style>
11949body{background:#FFFBEB; font-family:Poppins,sans-serif; color:white;}
11950.EDUTOTO-container{
11951max-width: 520px;
11952    margin: auto;
11953    padding: 15px;
11954    border-radius: 12px;
11955    background: #1a1508;
11956    border: 2px solid #EAB308;
11957    box-shadow: 0 0 20px #EAB308;
11958}
11959
11960.EDUTOTO-header{
11961  font-weight:900;
11962  text-align:center;
11963  color:#FBBF24;
11964  text-shadow:0 0 12px #C9A227;
11965  margin-bottom:12px;
11966}
11967
11968.EDUTOTO-controls{
11969  display:grid;
11970  grid-template-columns:1fr 1fr;
11971  gap:10px;
11972  margin-bottom:12px;
11973}
11974.EDUTOTO-controls label{
11975  display:flex; flex-direction:column;
11976  font-size:11px; color:#FDE68A;
11977}
11978.EDUTOTO-controls select,
11979.EDUTOTO-controls input{
11980  padding:8px;
11981  border-radius:8px;
11982  border:1px solid #FBBF24;
11983  background:#1a1508;
11984  color:white;
11985}
11986#btnGenerate{
11987  grid-column:span 2;
11988  padding:12px;
11989  background:#C9A227;
11990  border:none;
11991  color:#1a1508;
11992  font-weight:900;
11993  cursor:pointer;
11994  margin-top:5px;
11995  box-shadow:0 0 15px #EAB308;
11996}
11997
11998.EDUTOTO-result{
11999  margin-top:12px;
12000  padding:12px;
12001  border-radius:10px;
12002  background:rgba(234,179,8,0.05);
12003  border:1px solid #FBBF24;
12004}
12005
12006.EDUTOTO-main div, .EDUTOTO-details div{
12007  margin:5px 0;
12008}
12009
12010#rPrediction{
12011  font-size:24px;
12012  font-weight:900;
12013  color:#FBBF24;
12014}
12015
12016.EDUTOTO-alts{
12017  margin-top:10px;
12018}
12019.EDUTOTO-alts h4{margin-bottom:6px;}
12020#EDUTOTOAltList div{
12021  background:#1a1508;
12022  padding:6px 10px;
12023  margin:3px 0;
12024  border-radius:8px;
12025  border-left:3px solid #C9A227;
12026}
12027.bank-tab.active, .bank-tab:hover {
12028    background: #EAB308;
12029    color: #fff;
12030    border-color: #EAB308;
12031    box-shadow: 0 3px 10px -4px rgb(234 179 8 / 60%);
12032}
12033
12034.EDUTOTO-disclaimer{
12035  font-size:10px;
12036  margin-top:8px;
12037  text-align:center;
12038  color:#FDE68A;
12039}
12040</style>
12041
12042<script>
12043const leagues = ["Premier League","La Liga","Serie A"];
12044const tips = ["Analisis statistik tim","Performa terakhir","Cek head-to-head"];
12045function rand(arr){return arr[Math.floor(Math.random()*arr.length)];}
12046
12047var _bgBtn=document.getElementById("btnGenerate");if(_bgBtn)_bgBtn.onclick=function(){
12048  const type = document.getElementById("betTypeFilter").value;
12049  const league = rand(leagues);
12050
12051  let pred;
12052  if(type==="1X2") pred=rand(["1","X","2"]);
12053  else if(type==="OVER_UNDER") pred=rand(["Over","Under"]);
12054  else pred=rand(["Home -1","Away +1","Draw"]);
12055
12056  document.getElementById("rLeague").innerText=league;
12057  document.getElementById("rPrediction").innerText=pred;
12058  document.getElementById("rBetType").innerText=type;
12059  document.getElementById("rConf").innerText=Math.floor(Math.random()*40+60)+"%";
12060  document.getElementById("rTip").innerText=rand(tips);
12061  document.getElementById("rOdds").innerText=(Math.random()*3+1).toFixed(2);
12062
12063  // alternatif
12064  const alt=document.getElementById("EDUTOTOAltList"); alt.innerHTML="";
12065  for(let i=0;i<3;i++){let d=document.createElement("div"); d.innerText=rand(["1","X","2"]); alt.appendChild(d);}
12066  
12067  document.getElementById("EDUTOTOStatus").innerText="— READY —";
12068}
12069</script>
12069
12070
12071
12072                        
12073            <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>
12074        </div>
12075        </div>
12076        </section>
12077
12078        <aside class="pd-sidebar">
12079            <div class="pd-side-card pd-buy-card">
12080                <form class="pd-buy-form" id="pd-buy-form" data-pid="325">
12081                    <input type="hidden" name="variant_id" id="pd-variant-id" value="0">
12082                    <div>
12083                        <label for="qty" class="qty-label">Qty</label>
12084                        <input type="number" id="qty" name="qty" value="0" min="1"
12085                               max="0"
12086                               disabled>
12087                    </div>
12088                    <div class="pd-actions">
12089                        <button type="submit" class="btn-add-cart" id="btn-add-cart" disabled>+ Keranjang</button>
12090                        <button type="button" class="btn-buy" id="btn-buy" data-id="278426" disabled >Beli</button>
12091                        <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>
12092                                            </div>
12093                </form>
12094                <div class="paylater-promo paylater-promo-md paylater-desktop-only">
12095    <span class="paylater-icon">💳</span>
12096    <span class="paylater-text">
12097        <strong>Autorized EDUTOTO</strong>
12098        <small>Situs Resmi EDUTOTO </small>
12099    </span>
12100</div>            </div>
12101
12102                        
12103                        <div class="pd-side-card qoala-banner-card">
12104                <div class="qoala-banner">
12105                    <div class="qoala-banner-content">
12106                        <div class="qoala-icon">
12107                            <center><img src="/img/favicon.png" alt="EDUTOTO" class="qoala-shield-img"></center>
12108                        </div>
12109                        <div class="qoala-text">
12110                            <div class="qoala-title">Login EDUTOTO</div>
12111                            <div class="qoala-subtitle">Verified</div>
12112                        </div>
12113                    </div>
12114                    <a href="https://relaksasi.norita.mx/" class="qoala-cta"
12115                       data-ga-event="qoala_banner_click"
12116                       data-product-id="325"
12117                       data-product-name="EDUTOTO"
12118                       data-category-path="Komputer &amp; Laptop/Laptop/Gaming" target="_blank">Info</a>
12119                </div>
12120<style>
12121.qoala-banner-card{
12122  padding:0;
12123  overflow:hidden;
12124}
12125
12126.qoala-banner{
12127  background:linear-gradient(135deg, #EAB308 0%, #FBBF24 100%);
12128  padding:12px 16px;
12129  display:flex;
12130  align-items:center;
12131  justify-content:space-between;
12132  color:white;
12133  text-decoration:none;
12134  min-height:60px;
12135}
12136
12137.qoala-banner-content{
12138  display:flex;
12139  align-items:center;
12140  gap:12px;
12141}
12142
12143.qoala-icon{
12144  display:flex;
12145  align-items:center;
12146  justify-content:center;
12147}
12148
12149.qoala-shield-img{
12150  width:36px;
12151  height:36px;
12152  object-fit:contain;
12153  opacity:.9;
12154}
12155
12156.qoala-title{
12157  font-weight:600;
12158  font-size:14px;
12159  line-height:1.2;
12160}
12161
12162.qoala-subtitle{
12163  font-size:12px;
12164  opacity:.9;
12165  line-height:1.2;
12166}
12167
12168.qoala-cta{
12169  background:rgba(255,255,255,.15);
12170  color:white;
12171  padding:6px 12px;
12172  border-radius:6px;
12173  font-size:12px;
12174  font-weight:600;
12175  text-decoration:none;
12176  transition:background .2s ease;
12177  border:1px solid rgba(234,179,8,.4);
12178}
12179
12180.qoala-cta:hover{
12181  background:rgba(251,191,36,.25);
12182  color:white;
12183}
12184
12185@media(max-width:768px){
12186  .qoala-banner{
12187    padding:10px 12px;
12188  }
12189
12190  .qoala-title{
12191    font-size:17px;
12192  }
12193
12194  .qoala-subtitle{
12195    font-size:11px;
12196  }
12197}
12198</style>
12199            </div>
12200            
12201                        <div class="pd-side-card pd-installment-card2"
12202                 id="inst-card"
12203                 data-base-price="20789000"
12204                 data-zero-smart="0">
12205                <div class="inst-head">
12206                    <h3>Promo & Fitur EDUTOTO</h3>
12207                                    </div>
12208
12209                <div class="inst-bank-tabs" role="tablist" aria-label="Bank">
12210                    <button type="button" class="bank-tab active" data-bank="Lucky Draw" aria-selected="true">Lucky Draw</button>
12211                    <button type="button" class="bank-tab" data-bank="Lomba Panjat Tur
12211nover" aria-selected="false">Lomba Panjat Turnover</button>
12212                    <button type="button" class="bank-tab" data-bank="RTP Gacor" aria-selected="false">RTP Gacor</button>
12213                </div>
12214
12215                <ul class="inst-month-list" id="inst-month-list" aria-label="Daftar cicilan per bulan"></ul>
12216                <div class="inst-note" id="inst-zero-note" style="display:none;">0% Cicilan khusus bank terpilih*</div>
12217            </div>
12218            
12219            <!-- MECHA GOLD FINAL BOSS REVIEW SLIDER -->
12220<div class="tnt-review-mecha">
12221
12222<div class="tnt-header">
12223<h2>REVIEW MEMBER EDUTOTO</h2>
12224
12225</div>
12226
12227<div class="tnt-slider" id="tntSlider">
12228
12229  <div class="tnt-slide active">
12230  <div class="tnt-name">Raka Putra – Bandung</div>
12231  <div class="tnt-comment">"Sejak pertama masuk, EDUTOTO terasa nyaman. Menu-menunya rapi, aksesnya cepat, dan saya bisa langsung bermain tanpa ribet."</div>
12232</div>
12233
12234<div class="tnt-slide">
12235  <div class="tnt-name">Nadia Kusuma – Surabaya</div>
12236  <div class="tnt-comment">"Tampilannya elegan dan tidak bikin pusing. Saya main di HP setiap hari, alurnya ringan, dan suasana relaksasinya benar-benar terasa."</div>
12237</div>
12238
12239<div class="tnt-slide">
12240  <div class="tnt-name">Fajar Santoso – Yogyakarta</div>
12241  <div class="tnt-comment">"Yang bikin saya betah di EDUTOTO adalah sistemnya stabil dan fiturnya lengkap. Login sampai transaksi terasa praktis, jadi mainnya lebih tenang."</div>
12242</div>
12243
12244
12245
12246
12247</div>
12248</div>
12249
12250</aside>
12251</div>
12252
12253<style>
12254.tnt-review-mecha{
12255  background: radial-gradient(circle at center, #EAB308, #1a1508);
12256  border: 2px solid #CA8A04;
12257  border-radius: 14px;
12258  padding: 20px;
12259  color: #dbdbdb;
12260  font-family: 'Orbitron', sans-serif;
12261  position: relative;
12262  overflow: hidden;
12263  box-shadow: 0 0 25px #EAB308, inset 0 0 20px rgba(234, 179, 8, 0.2);
12264}
12265
12266.tnt-review-mecha::before{
12267  content:"";
12268  position:absolute;
12269  inset:0;
12270  background: repeating-linear-gradient(
12271    to bottom,
12272    rgba(234,179,8,0.08),
12273    rgba(234,179,8,0.08) 1px,
12274    transparent 2px,
12275    transparent 4px
12276  );
12277  pointer-events:none;
12278}
12279
12280.bank-tab.active,
12281.bank-tab:hover
12282
12283.tnt-header h2{
12284  font-size:18px;
12285  color:#000000;
12286  text-shadow:0 0 10px #FBBF24, 0 0 20px #EAB308;
12287  margin-bottom:5px;
12288}
12289
12290.tnt-header p{
12291  font-size:12px;
12292  opacity:.85;
12293  margin-bottom:15px;
12294}
12295
12296.tnt-slider{
12297  position:relative;
12298  height:90px;
12299}
12300
12301.tnt-slide{
12302  position:absolute;
12303  width:100%;
12304  opacity:0;
12305  transform:translateX(40px);
12306  transition:all .6s ease;
12307}
12308
12309.tenor-badge{
12310  background:#EAB308;
12311  color:#fff;
12312  font-weight:700;
12313  min-width:42px;
12314  text-align:center;
12315  padding:.38rem .4rem;
12316  font-size:.8rem;
12317  border-radius:10px;
12318  box-shadow:0 3px 10px -4px rgb(234 179 8);
12319}
12320
12321.tnt-slide.active{
12322  opacity:1;
12323  transform:translateX(0);
12324}
12325
12326.tnt-name{
12327  font-size:14px;
12328  color:#000000;
12329  margin-bottom:5px;
12330  text-shadow:0 0 6px #5a0080;
12331}
12332
12333.tnt-comment{
12334  font-size:13px;
12335  line-height:1.5;
12336}
12337
12338@keyframes pulseGlow{
12339  0%{box-shadow:0 0 10px #EAB308;}
12340  50%{box-shadow:0 0 25px #FBBF24;}
12341  100%{box-shadow:0 0 10px #EAB308;}
12342}
12343
12344.tnt-review-mecha{
12345  animation:pulseGlow 2.5s infinite;
12346}
12347</style>
12348
12349<script>
12350let slides = document.querySelectorAll('.tnt-slide');
12351let index = 0;
12352
12353setInterval(()=>{
12354  slides[index].classList.remove('active');
12355  index = (index + 1) % slides.length;
12356  slides[index].classList.add('active');
12357}, 3000);
12358</script>
12358
12359
12360        <div class="artikel-glow-santaaa">
12361
12362  <div class="artikel-shine"></div>
12363
12364  <h1>EDUTOTO - Sensasi Relaksasi Premium Dengan Akses Nyaman & Fitur Lengkap</h1>
12365<p style="text-align: justify;"><a href="https://norita.mx/">EDUTOTO</a> menghadirkan ruang digital berkelas yang menonjolkan suasana relaksasi. Setiap detail disusun supaya pengguna langsung merasa betah, mulai dari akses yang ringkas hingga kumpulan fitur yang tersedia dalam satu platform.</p>
12366
12367<p style="text-align: justify;">Tampilan yang bersih dan kekinian membuat alur bermain terasa ringan. Pengguna bisa masuk ke berbagai permainan tanpa proses yang bertele-tele. Pemain baru maupun yang sudah rutin bermain tetap mudah menemukan menu yang diinginkan.</p>
12368
12369<p style="text-align: justify;">Sensasi relaksasi premium terbentuk dari visual yang anggun, katalog game pilihan, serta sistem yang terus disegarkan. Hasilnya, setiap sesi terasa tenang, stabil, dan tetap menyenangkan dari awal hingga akhir.</p>
12370
12371<p style="text-align: justify;">Akses EDUTOTO tersedia di komputer maupun ponsel. Layout yang menyesuaikan layar, navigasi yang mudah dipahami, dan transaksi yang ringan membuat login sampai bermain tetap praktis kapan pun dibutuhkan.</p>
12372
12373<p style="text-align: justify;">Jika Anda menginginkan tempat bermain yang lebih santai sekaligus terpercaya, EDUTOTO menjadi pilihan yang pas. Kombinasi akses cepat, layanan premium, dan reputasi baik memberi rasa nyaman sekaligus keyakinan di setiap kunjungan.</p>
12374</div>
12375
12376<style>
12377.artikel-glow-santaaa{
12378      max-width: 820px;
12379    margin: 40px auto;
12380    padding: 36px;
12381    box-sizing: border-box;
12382    position: relative;
12383    overflow: hidden;
12384    border-radius: 30px;
12385    font-family: Arial, sans-serif;
12386    color: #ffffff;
12387    background: radial-gradient(circle at top left, rgba(234,179,8,.35), transparent 20%), radial-gradient(circle at bottom right, rgb(120,53,15), transparent 28%), linear-gradient(135deg, #EAB308, #8B6914, #FBBF24);
12388    border: 2px solid rgba(255, 255, 255, .55);
12389    box-shadow: 0 0 25px rgb(234 179 8), 0 0 55px rgb(251 191 36), 0 20px 45px rgba(0, 0, 0, .25), inset 0 0 35px rgba(139 105 20 / .45);
12390}
12391
12392.artikel-glow-santaaa:before{
12393  content:"";
12394  position:absolute;
12395  inset:10px;
12396  border-radius:24px;
12397  border:1px solid rgba(255,255,255,.32);
12398  pointer-events:none;
12399}
12400
12401.artikel-shine{
12402  position:absolute;
12403  top:-120px;
12404  left:-180px;
12405  width:280px;
12406  height:650px;
12407  background:linear-gradient(120deg,transparent,rgba(255,255,255,.55),transparent);
12408  transform:rotate(25deg);
12409  animation:artikelShine 4.5s infinite;
12410  pointer-events:none;
12411}
12412
12413@keyframes artikelShine{
12414  0%{left:-220px;}
12415  55%{left:110%;}
12416  100%{left:110%;}
12417}
12418
12419.artikel-glow-santaaa h1,
12420.artikel-glow-santaaa h2{
12421  position:relative;
12422  color:#000000;
12423  text-align:center;
12424  line-height:1.35;
12425  text-shadow:
12426    0 0 8px rgba(234 179 8 / 95%),
12427    0 0 18px rgb(251 191 36),
12428    0 2px 8px rgb(30 64 175);
12429}
12430
12431.artikel-glow-santaaa h1{
12432  font-size:31px;
12433  margin:0 0 20px;
12434}
12435
12436.artikel-glow-santaaa h2{
12437  font-size:24px;
12438  margin:28px 0 14px;
12439}
12440
12441.artikel-glow-santaaa p{
12442  position:relative;
12443  margin:0 0 16px;
12444  color:#f8ffff;
12445  font-size:15px;
12446  line-height:1.85;
12447  text-align:justify;
12448  text-shadow:0 1px 4px rgba(0,0,0,.28);
12449}
12450
12451.artikel-glow-santaaa a{
12452  color:#000000;
12453  font-weight:800;
12454  text-shadow:0 0 8px rgba(234,179,8,.75);
12455}
12456
12457.artikel-glow-santaaa img{
12458  position:relative;
12459  display:block;
12460  max-width:100%;
12461  margin:16px auto 24px;
12462  border-radius:16px;
12463  border:1px solid rgba(255,255,255,.55);
12464  box-shadow:
12465    0 0 18px rgba(255,255,255,.55),
12466    0 12px 28px rgba(0,0,0,.28);
12467}
12468
12469.artikel-highlight{
12470  position:relative;
12471  margin:24px 0;
12472  padding:22px;
12473  border-radius:22px;
12474  background:rgba(255,255,255,.20);
12475  border:1px solid rgba(255,255,255,.45);
12476  box-shadow:
12477    inset 0 1px 0 rgba(255,255,255,.65),
12478    inset 0 0 22px rgba(255,255,255,.14),
12479    0 10px 25px rgba(0,0,0,.18);
12480  backdrop-filter:blur(8px);
12481}
12482
12483.artikel-highlight:before{
12484  content:"";
12485  position:absolute;
12486  left:0;
12487  right:0;
12488  top:0;
12489  height:45%;
12490  border-radius:22px 22px 0 0;
12491  background:linear-gradient(rgba(255,255,255,.35),rgba(255,255,255,.04));
12492  pointer-events:none;
12493}
12494
12495.artikel-glow-santaaa ul{
12496  position:relative;
12497  margin:15px 0 0 20px;
12498  padding:0;
12499}
12500
12501.artikel-glow-santaaa li{
12502  margin-bottom:11px;
12503  padding:10px 12px;
12504  border-radius:12px;
12505  background:rgba(255,255,255,.16);
12506  color:#ffffff;
12507  line-height:1.6;
12508  box-shadow:inset 0 0 10px rgba(255,255,255,.08);
12509}
12510
12511@media(max-width:600px){
12512  .artikel-glow-santaaa{
12513    margin:25px 12px;
12514    padding:25px;
12515    border-radius:24px;
12516  }
12517
12518  .artikel-glow-santaaa h1{
12519    font-size:24px;
12520  }
12521
12522  .artikel-glow-santaaa h2{
12523    font-size:21px;
12524  }
12525}
12526</style>
12527
12528<div class="santaaa-faq-rainbow">
12529  <div class="santaaa-shine"></div>
12530
12531  <div class="santaaa-faq-top">
12532    <span>★ PREMIUM FAQ ★</span>
12533    <h2>Pertanyaan Umum EDUTOTO</h2>
12534  </div>
12535
12536  <div class="santaaa-rainbow-item">
12537    <button type="button">Apa yang membuat pengalaman EDUTOTO terasa lebih premium?</button>
12538    <div class="santaaa-rainbow-answer">
12539      <p>EDUTOTO memadukan desain elegan, akses yang ringkas, dan fitur lengkap dalam satu platform. Kombinasi itu membuat setiap sesi terasa lebih tenang, nyaman, dan berkelas sejak pengguna pertama kali masuk.</p>
12540    </div>
12541  </div>
12542
12543  <div class="santaaa-rainbow-item">
12544    <button type="button">Apakah pemula bisa langsung bermain di EDUTOTO?</button>
12545    <div class="santaaa-rainbow-answer">
12546      <p>Bisa. Antarmuka EDUTOTO sederhana dan modern, sehingga pemula tidak perlu melalui langkah yang berbelit. Menu favorit mudah ditemukan, dan alur bermain tetap jelas bagi pemain yang sudah aktif.</p>
12547    </div>
12548  </div>
12549
12550  <div class="santaaa-rainbow-item">
12551    <button type="button">Bagaimana EDUTOTO menjaga suasana relaksasi saat bermain?</button>
12552    <div class="santaaa-rainbow-answer">
12553      <p>Nuansa relaksasi dijaga lewat tampilan yang rapi, pilihan game berkualitas, dan sistem yang selalu diperbarui. Performa yang stabil membuat sesi bermain terasa tenang tanpa gangguan yang mengganggu kenyamanan.</p>
12554    </div>
12555  </div>
12556
12557  <div class="santaaa-rainbow-item">
12558    <button type="button">Apakah EDUTOTO bisa dibuka di HP dan komputer?</button>
12559    <div class="santaaa-rainbow-answer">
12560      <p>Ya. EDUTOTO mendukung akses di smartphone maupun komputer. Tampilan responsif, navigasi yang intuitif, dan proses transaksi yang ringan membuat login hingga bermain tetap praktis di perangkat mana pun.</p>
12561    </div>
12562  </div>
12563
12564  <div class="santaaa-rainbow-item">
12565    <button type="button">Mengapa EDUTOTO cocok dijadikan pilihan utama?</button>
12566    <div class="santaaa-rainbow-answer">
12567      <p>EDUTOTO cocok karena menawarkan akses cepat, fitur premium, dan reputasi positif. Bagi siapa pun yang mencari platform bermain yang lebih santai dan terpercaya, pengalaman di sini terasa lebih nyaman sekaligus meyakinkan.</p>
12568    </div>
12569  </div>
12570</div>
12571
12572<style>
12573.santaaa-faq-rainbow{
12574      max-width: 760px;
12575    margin: 40px auto;
12576    padding: 30px;
12577    box-sizing: border-box;
12578    position: relative;
12579    overflow: hidden;
12580    border-radius: 32px;
12581    font-family: Arial, sans-serif;
12582    background: radial-gradient(circle at 15% 10%, rgba(234,179,8,.35), transparent 18%), radial-gradient(circle at 85% 15%, rgb(251,191,36), transparent 25%), radial-gradient(circle at 20% 90%, rgb(234,179,8), transparent 25%), linear-gradient(135deg, #EAB308, #8B6914, #78350F, #FBBF24);
12583    border: 2px solid rgb(234,179,8);
12584    box-shadow: 0 0 25px rgb(234,179,8), 0 0 55px rgba(251,191,36,.7), inset 0 0 35px rgba(139,105,20,.45);
12585}
12586
12587.santaaa-shine{
12588  position:absolute;
12589  top:-90px;
12590  left:-120px;
12591  width:260px;
12592  height:520px;
12593  background:linear-gradient(120deg,transparent,rgba(255,255,255,.55),transparent);
12594  transform:rotate(25deg);
12595  animation:KTShine 4s infinite;
12596  pointer-events:none;
12597}
12598
12599@keyframes KTShine{
12600  0%{left:-180px;}
12601  55%{left:110%;}
12602  100%{left:110%;}
12603}
12604
12605.santaaa-faq-top{
12606  position:relative;
12607  text-align:center;
12608  margin-bottom:24px;
12609}
12610
12611.santaaa-faq-top span{
12612  display:inline-block;
12613  padding:8px 22px;
12614  border-radius:999px;
12615  background:rgba(234,179,8,0);
12616  color:#dbdbdb;
12617  font-size:12px;
12618  font-weight:900;
12619  letter-spacing:2px;
12620  border:1px solid rgba(215,215,215,.62);
12621  box-shadow:inset 0 0 14px rgba(255,255,255,.25);
12622}
12623
12624.santaaa-faq-top h2{
12625  margin:16px 0 8px;
12626  color:#000000;
12627  font-size:34px;
12628  line-height:1.15;
12629  text-shadow:
12630    0 0 10px rgb(234,179,8),
12631    0 0 22px rgb(251,191,36);
12632}
12633
12634.santaaa-faq-top p{
12635  margin:0;
12636  color:#f3ffff;
12637  font-size:14px;
12638}
12639
12640.santaaa-rainbow-item{
12641  position:relative;
12642  overflow:hidden;
12643  margin-bottom:14px;
12644  border-radius:20px;
12645  background:rgba(255,255,255,.20);
12646  border:1px solid rgba(255,255,255,.48);
12647  box-shadow:
12648    inset 0 1px 0 rgba(255,255,255,.6),
12649    0 10px 25px rgba(0,0,0,.18);
12650  backdrop-filter:blur(10px);
12651}
12652
12653.santaaa-rainbow-item:before{
12654  content:"";
12655  position:absolute;
12656  top:0;
12657  left:0;
12658  right:0;
12659  height:45%;
12660  background:linear-gradient(rgba(255,255,255,.32),rgba(255,255,255,.04));
12661  pointer-events:none;
12662}
12663
12664.santaaa-rainbow-item button{
12665  width:100%;
12666  padding:18px 58px 18px 20px;
12667  border:0;
12668  background:#8B6914;
12669  color:#dbdbdb;
12670  text-align:left;
12671  cursor:pointer;
12672  font-size:15px;
12673  font-weight:900;
12674  position:relative;
12675  z-index:2;
12676  text-shadow:0 2px 6px rgba(0,0,0,.35);
12677}
12678
12679.santaaa-rainbow-item button:after{
12680  content:"+";
12681  position:absolute;
12682  right:18px;
12683  top:50%;
12684  transform:translateY(-50%);
12685  width:34px;
12686  height:34px;
12687  border-radius:50%;
12688  background:linear-gradient(135deg,#EAB308,#8B6914);
12689  color:#d1d1d1;
12690  text-align:center;
12691  line-height:34px;
12692  font-size:24px;
12693  font-weight:900;
12694  box-shadow:
12695    0 0 14px rgba(255,255,255,.8),
12696    inset 0 -4px 8px rgba(0,0,0,.18);
12697}
12698
12699.santaaa-rainbow-item.active button:after{
12700  content:"−";
12701  background:linear-gradient(135deg,#FBBF24,#78350F);
12702}
12703
12704.santaaa-rainbow-answer{
12705  max-height:0;
12706  overflow:hidden;
12707  transition:max-height .35s ease;
12708  position:relative;
12709  z-index:2;
12710}
12711
12712.santaaa-rainbow-item.active .santaaa-rainbow-answer{
12713  max-height:170px;
12714}
12715
12716.santaaa-rainbow-answer p{
12717  margin:0;
12718  padding:0 20px 18px;
12719  color:#dbdbdb;
12720  font-size:14px;
12721  line-height:1.7;
12722  text-shadow:0 2px 5px rgba(0,0,0);
12723}
12724
12725@media(max-width:600px){
12726  .santaaa-faq-rainbow{
12727    margin:25px 12px;
12728    padding:22px;
12729    border-radius:24px;
12730  }
12731
12732  .santaaa-faq-top h2{
12733    font-size:26px;
12734  }
12735}
12736</style>
12737
12738<script>
12739document.querySelectorAll(".santaaa-rainbow-item button").forEach(function(btn){
12740  btn.addEventListener("click", function(){
12741    this.parentElement.classList.toggle("active");
12742  });
12743});
12744</script>
12744
12745<style>
12746@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&display=swap');
12747
12748.cyber-mecha {
12749  max-width: 800px;
12750  margin: 40px auto;
12751
12752  /* deep luxury red */
12753  background: radial-gradient(circle at top, #14b1d7, #14b1d7 70%);
12754
12755  border-radius: 16px;
12756  border: 1px solid rgba(255, 90, 90, 0.35);
12757
12758  box-shadow:
12759    0 0 25px rgba(140, 30, 30, 0.35),
12760    inset 0 0 20px rgba(255, 60, 60, 0.10);
12761
12762  padding: 25px;
12763  font-family: 'Orbitron', sans-serif;
12764  color: #f7e6e6;
12765  position: relative;
12766  overflow: hidden;
12767}
12768
12769/* scanline overlay */
12770.cyber-mecha::before {
12771  content: "";
12772  position: absolute;
12773  inset: 0;
12774  background: repeating-linear-gradient(
12775    to bottom,
12776    rgba(255,90,90,0.03),
12777    rgba(255,90,90,0.03) 1px,
12778    transparent 1px,
12779    transparent 3px
12780  );
12781  pointer-events: none;
12782}
12783
12784.pd-full-desc-title {
12785  text-align: center;
12786  font-size: 22px;
12787
12788  /* elegant muted red */
12789  color: #e08b8b;
12790
12791  letter-spacing: 2px;
12792  text-shadow: 0 0 10px rgba(224, 139, 139, 0.4);
12793  margin-bottom: 20px;
12794}
12795
12796.mecha-header h3 {
12797  text-align: center;
12798  font-size: 18px;
12799  color: #ffb3b3;
12800  text-shadow: 0 0 8px rgba(255, 90, 90, 0.5);
12801  margin-bottom: 15px;
12802}
12803
12804.mecha-image img {
12805  width: 100%;
12806  border-radius: 10px;
12807  border: 1px solid rgba(255, 90, 90, 0.4);
12808  box-shadow: 0 0 20px rgba(255, 60, 60, 0.25);
12809  margin-bottom: 20px;
12810}
12811
12812.mecha-content p {
12813  font-size: 14px;
12814  line-height: 1.8;
12815  margin-bottom: 14px;
12816  color: #f3dede;
12817}
12818
12819.glow {
12820  color: #e08b8b;
12821  text-shadow: 0 0 6px rgba(224, 139, 139, 0.5);
12822  font-weight: 600;
12823}
12824
12825.glow-strong {
12826  color: #ff6b6b;
12827  text-shadow:
12828    0 0 10px rgba(255, 90, 90, 0.6),
12829    0 0 20px rgba(255, 60, 60, 0.4);
12830  font-weight: 700;
12831}
12832
12833.final-line {
12834  margin-top: 18px;
12835  padding: 10px;
12836  border: 1px solid rgba(255, 90, 90, 0.35);
12837  background: rgba(60, 0, 0, 0.25);
12838  border-radius: 8px;
12839  text-align: center;
12840  color: #ffd0d0;
12841}
12842</style>
12843    
12844    
12845    
12846<section class="pd-related-full">
12847    <h2 class="pd-related-title">Provider Game Populer</h2>
12848    <div class="pd-related-grid pd-related-grid--full">
12849
12850        <a class="pd-related-card" href="https://norita.mx/">
12851            <div class="thumb">
12852                <img src="img/banner-edutoto.webp" alt="Pragmatic Play">
12853                <span class="mini-disc">185+</span>
12854            </div>
12855            <div class="info">
12856                <h3>Pragmatic Play</h3>
12857                <div class="spec-chips">Game Slot <span class="spec-divider">|</span> Populer <span class="spec-divider">|</span> Update Rutin <span class="spec-divider">|</span> Favorit</div>
12858                <span class="price">
12859                    <span class="final">385 Game</span>
12860                    <span class="ori">402 Judul</span>
12861                </span>
12862                <span class="sold">Dimainkan 18.246+</span>
12863                <span class="stock-dot low" title="Populer"></span>
12864            </div>
12865        </a>
12866
12867        <a class="pd-related-card" href="https://norita.mx/">
12868            <div class="thumb">
12869                <img src="img/banner-edutoto.webp" alt="PG Soft">
12870                <span class="mini-disc">142+</span>
12871            </div>
12872            <div class="info">
12873                <h3>PG Soft</h3>
12874                <div class="spec-chips">Mobile Friendly <span class="spec-divider">|</span> Game Baru <span class="spec-divider">|</span> Populer <span class="spec-divider">|</span> Favorit</div>
12875                <span class="price">
12876                    <span class="final">214 Game</span>
12877                    <span class="ori">228 Judul</span>
12878                </span>
12879                <span class="sold">Dimainkan 15.913+</span>
12880                <span class="stock-dot ready" title="Tersedia"></span>
12881            </div>
12882        </a>
12883
12884        <a class="pd-related-card" href="https://norita.mx/">
12885            <div class="thumb">
12886                <img src="img/banner-edutoto.webp" alt="Habanero">
12887                <span class="mini-disc">96+</span>
12888            </div>
12889            <div class="info">
12890                <h3>Habanero</h3>
12891                <div class="spec-chips">EDUTOTO DAFTAR <span class="spec-divider">|</span> Koleksi Lengkap <span class="spec-divider">|</span> Terbaru <span class="spec-divider">|</span> Pilihan</div>
12892                <span class="price">
12893                    <span class="final">187 Game</span>
12894                    <span class="ori">195 Judul</span>
12895                </span>
12896                <span class="sold">Dimainkan 12.774+</span>
12897                <span class="stock-dot ready" title="Tersedia"></span>
12898            </div>
12899        </a>
12900
12901        <a class="pd-related-card" href="https://norita.mx/">
12902            <div class="thumb">
12903                <img src="img/banner-edutoto.webp" alt="Microgaming">
12904                <span class="mini-disc">210+</span>
12905            </div>
12906            <div class="info">
12907                <h3>Microgaming</h3>
12908                <div class="spec-chips">Game Klasik <span class="spec-divider">|</span> Provider Populer <span class="spec-divider">|</span> Terlengkap <span class="spec-divider">|</span> Update</div>
12909                <span class="price">
12910                    <span class="final">356 Game</span>
12911                    <span class="ori">369 Judul</span>
12912                </span>
12913                <span class="sold">Dimainkan 10.638+</span>
12914                <span class="stock-dot ready" title="Tersedia"></span>
12915            </div>
12916        </a>
12917
12918        <a class="pd-related-card" href="https://norita.mx/">
12919            <div class="thumb">
12920                <img src="img/banner-edutoto.webp" alt="Spadegaming">
12921                <span class="mini-disc">118+</span>
12922            </div>
12923            <div class="info">
12924                <h3>Spadegaming</h3>
12925                <div class="spec-chips">Game Favorit <span class="spec-divider">|</span> Provider Terbaik <span class="spec-divider">|</span> Populer <span class="spec-divider">|</span> Mudah Diakses</div>
12926                <span class="price">
12927                    <span class="final">168 Game</span>
12928                    <span class="ori">179 Judul</span>
12929                </span>
12930                <span class="sold">Dimainkan 9.427+</span>
12931                <span class="stock-dot low" title="Populer"></span>
12932            </div>
12933        </a>
12934
12935        <a class="pd-related-card" href="https://norita.mx/">
12936            <div class="thumb">
12937                <img src="img/banner-edutoto.webp" alt="CQ9 Gaming">
12938                <span class="mini-disc">134+</span>
12939            </div>
12940            <div class="info">
12941                <h3>CQ9 Gaming</h3>
12942                <div class="spec-chips">Game Terbaru <span class="spec-divider">|</span> Provider Pilihan <span class="spec-divider">|</span> Koleksi Lengkap <span class="spec-divider">|</span> Populer</div>
12943                <span class="price">
12944                    <span class="final">196 Game</span>
12945                    <span class="ori">207 Judul</span>
12946                </span>
12947                <span class="sold">Dimainkan 11.385+</span>
12948                <span class="stock-dot ready" title="Tersedia"></span>
12949            </div>
12950        </a>
12951
12952    </div>
12953</section>
12954    
12955    <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-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">
12955<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>
12956
12957    <button type="button"
12958            class="pd-scroll-top"
12959            id="pd-scroll-top"
12960            aria-label="Kembali ke atas"
12961            title="Kembali ke atas">
12962        <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
12963            <path fill="currentColor" d="M7.41 14.59 12 10l4.59 4.59L18 13.17l-6-6-6 6z"/>
12964        </svg>
12965    </button>
12966</main>
12967
12968<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="img/banner-edutoto.webp" alt="EDUTOTO" class="pd-share-preview-img"><div class="pd-share-preview-info"><h3 class="pd-share-preview-name">EDUTOTO</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"><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">
12968<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://norita.mx/" 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>
12969
12970<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:#0b1220;color:#eef3ff;border:1px solid rgba(255,255,255,.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:#EAB308;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 #EAB308}.toast.warn{border-left:4px solid #CA8A04}.toast .progress{position:absolute;left:0;bottom:0;height:3px;background:linear-gradient(90deg,#FBBF24,#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>
12971<style>
12972.toast .msg { word-break: break-word; overflow-wrap: anywhere; }
12973
12974.toast-root { bottom: max(12px, env(safe-area-inset-bottom, 12px)); }
12975
12976@media (max-width: 540px) {
12977  .toast-root {
12978    left: 0; right: 0; transform: none;
12979    padding: 0 12px;
12980    bottom: calc(max(12px, env(safe-area-inset-bottom, 12px)) + var(--fab-offset, 72px));
12981    max-width: none;
12982  }
12983  .toast {
12984    grid-template-columns: 1fr; /* 1 kolom */
12985    position: relative;
12986    padding: 14px 14px 12px 14px;
12987    border-radius: 16px;
12988  }
12989  .toast .ico {
12990    position: absolute; left: 14px; top: 14px;
12991    margin: 0; transform: translateY(1px);
12992  }
12993  .toast .cnt { padding-left: 34px; }
12994  .toast .ttl { font-size: 15px; margin-bottom: 6px; }
12995  .toast .msg { font-size: 13px; opacity: .96; }
12996  .toast .actions {
12997    margin-top: 12px; gap: 10px; width: 100%;
12998    display: grid; grid-template-columns: 1fr 1fr;
12999  }
13000  .toast .btn { width: 100%; min-width: 0; padding: 10px 0; }
13001  .toast .close { position: absolute; right: 6px; top: 6px; }
13002}
13003</style>
13004
13005<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
13006(function adjustToastFabOffset(){
13007  const root = document.getElementById('toastRoot');
13008  if (!root) return;
13009
13010  function isVisible(el){
13011    if (!el) return false;
13012    const cs = getComputedStyle(el);
13013    if (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0') return false;
13014    const r = el.getBoundingClientRect();
13015    return r.width > 0 && r.height > 0;
13016  }
13017
13018  function update(){
13019    const fab = document.getElementById('pd-mobile-fab');
13020    let extra = 16;
13021    if (isVisible(fab)) {
13022      const h = Math.round(fab.getBoundingClientRect().height) || 56;
13023      extra = h + 16; // FAB height + margin
13024    }
13025    root.style.setProperty('--fab-offset', extra + 'px');
13026  }
13027
13028  window.addEventListener('resize', update, { passive: true });
13029  const fab = document.getElementById('pd-mobile-fab');
13030  if (window.ResizeObserver && fab) {
13031    const ro = new ResizeObserver(update);
13032    ro.observe(fab);
13033  }
13034  
13035  update();
13036})();
13037</script>
13037
13038<div class="toast-root" id="toastRoot" aria-live="polite" aria-atomic="true"></div>
13039
13040<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="#CA8A04"><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="#EAB308"><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="#EAB308"/></svg>'}
13041window.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>`;
13042
13043    const close = ()=> {
13044      if (!el.parentNode) return;
13045      el.style.transition = 'transform .2s ease, opacity .2s ease';
13046      el.style.opacity = '0'; el.style.transform = 'translateY(10px)';
13047      setTimeout(()=>{ if (el.parentNode) el.parentNode.removeChild(el); }, 220);
13048    };
13049
13050    el.querySelector('.close').addEventListener('click', close);
13051    el.querySelectorAll('.actions .btn').forEach(btn=>{
13052      btn.addEventListener('click', ()=>{
13053        const lab = btn.getAttribute('data-act');
13054        try {
13055          (o.actions||[]).forEach(a=>{ if (a.label===lab && typeof a.onClick==='function') a.onClick(); });
13056        } catch(_){}
13057        close();
13058      });
13059    });
13060
13061    root.appendChild(el);
13062    setTimeout(close, o.duration);
13063    return el;
13064  };
13065})();
13066
13067const _0x1a2b=[{"url":"img/banner-edutoto.webp","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":"img/banner-edutoto.webp","alt":"Lenovo Lenovo Legion Go 8ASP2 - 83N0000RID /AMD Ryzen Z2 Extreme/32GB/1TB SSD/AMD Radeon Graphics/8.8\" WUXGA OLED Touch/Wi...","slug":"img/banner-edutoto.webp"},{"url":"img/banner-edutoto.webp","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":"img/banner-edutoto.webp","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":"img/banner-edutoto.webp","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":"img/banner-edutoto.webp","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-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=>
13067c.textContent.trim()).filter(Boolean);return b.length?b.join(' / '):''}
13068function encodeVariantHash(id){if(!id||id<=0)return '';const xor=id^0x4A5D;return xor.toString(36)}
13069function decodeVariantHash(hash){if(!hash)return 0;const xor=parseInt(hash,36);return isNaN(xor)?0:xor^0x4A5D}
13070
13071function 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})}
13072
13073function executeBuyNowLegacy(id,variantId){const a=getVariantSummaryText(),b=(function(){const c=document.getElementById('pd-sku');return c?c.textContent.trim():_0x5e6f})();
13074
13075    let willRedirect = false;
13076    try {
13077        if (window.addToCart) {
13078            window.addToCart(id, 1, variantId);
13079            willRedirect = false;
13080        }
13081    } catch(_){}
13082
13083    if (willRedirect) {
13084        showToast({
13085            type: 'info',
13086            title: 'Menuju Keranjang',
13087            message: `<strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}<br>SKU: ${escapeHtml(skuText)} · Qty: 1`,
13088            duration: 1800
13089        });
13090        setTimeout(()=>{ window.location.href='/keranjang'; }, 1200);
13091    } else {
13092        showToast({
13093            type: 'warn',
13094            title: 'Fitur Beli Langsung Segera Hadir',
13095            message: `Sementara ini Anda bisa menambahkan produk ke keranjang terlebih dahulu.<br><strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}`,
13096            duration: 6000,
13097            actions: [
13098                { label: '+ Keranjang', onClick: ()=> { try{ if (window.addToCart) window.addToCart(id,1,variantId); }catch(_){} } },
13099                { label: 'Hubungi CS', variant: 'alt', onClick: ()=> { window.open('https://wa.me/6281380009898','_blank'); } }
13100            ]
13101        });
13102    }
13103}
13104
13105document.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)})}});
13106
13107function _0x2m3n(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[m]))}
13108
13109(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')}})})();
13110
13111(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;
13111slides.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})})();
13112
13113/* ================= Simulasi Cicilan ================= */
13114(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'}
13115
13116function _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'}
13117
13118card.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)});
13119
13120window.instSetBasePrice=function(newPrice){setBasePrice(newPrice);_0x4o5p(activeBank())};_0x4o5p(activeBank());
13121})();
13122
13123/* ====== Fokus gambar varian ====== */
13124function _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 ''}}}
13125function _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}}
13126  if (c){
13127    const targetName = _0x8s9t(url);
13128    const slideImg = Array.from(c.querySelectorAll('img')).find(im => _0x8s9t(im.src) === targetName);
13129    if (slideImg){
13130      
13131      try {
13132        const container = c;
13133        const imgRect = slideImg.getBoundingClientRect();
13134        const contRect = container.getBoundingClientRect();
13135        const offsetLeft = slideImg.offsetLeft - container.offsetLeft;
13136        const center = offsetLeft - (container.clientWidth - slideImg.clientWidth) / 2;
13137        container.scrollTo({ left: Math.max(0, center), behavior: 'smooth' });
13138      } catch(_){
13139        
13140        slideImg.scrollIntoView({ behavior:'smooth', inline:'center', block:'nearest' });
13141      }
13142    }
13143  }
13144}
13145function findImageForVariant(variant){
13146  if (!variant || !variant.attrs) return null;
13147  const tokens = [];
13148  try{
13149    Object.values(variant.attrs).forEach(v=>{
13150      if (!v) return;
13151      const raw = String(v);
13152      tokens.push(raw);
13153      tokens.push(normalize(raw));
13154    });
13155  }catch(_){}
13156  if (variant.sku){
13157    tokens.push(String(variant.sku));
13158    tokens.push(normalize(String(variant.sku)));
13159  }
13160  if (variant.image_url) {
13161      tokens.push(String(variant.image_url));
13162  }
13163  let best = null;
13164  let bestScore = -1;
13165
13166  for (const g of (galleryData||[])){
13167    const slugN = normalize(g.slug||'');
13168    const altN  = normalize(g.alt||'');
13169    const file  = _0x8s9t(g.url||'');
13170    const fileN = normalize(file);
13171    const imgU  = g.url;
13172
13173    for (const t of tokens){
13174      const tN = normalize(t);
13175      if (!tN) continue;
13176
13177      let score = -1;
13178      if (imgU === tN) score = 300;
13179      if (slugN && slugN === tN) score = 300;
13180      else if (fileN && fileN.includes(tN)) score = 200;
13181      else if (altN && altN.includes(tN)) score = 120;
13182
13183      if (score > bestScore){
13184        bestScore = score;
13185        best = g;
13186      }
13187      if (bestScore >= 300) break;
13188    }
13189    if (bestScore >= 300) break;
13190  }
13191  return bestScore >= 120 ? best : null;
13192}
13193function _0xey3z(variant){const a=_0xcw2x(variant);if(a&&a.url){_0xau1v(a.url,a.alt||'')}}
13194
13195/* ====== Layout mobile ====== */
13196(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;
13197
13198    let pricePlaced = false;
13199    let varPlaced = false;
13200    let titlePlaced = false;
13201    let promoPlaced = false;
13202    const mq = window.matchMedia('(max-width: 640px)');
13203
13204    function apply(){
13205        if (mq.matches) {
13206            if (!titlePlaced && a && titleRow && d.contains(titleRow)) { try{a.appendChild(titleRow);titlePlaced=true;}catch(_){} }
13207            if (!pricePlaced && a && b) { try{a.appendChild(b);pricePlaced=true;}catch(_){} }
13208            if (!varPlaced && f && d && e) {
13209                try{
13210                    f.hidden = false;
13211                    if (i && !f.contains(i)) f.appendChild(i);
13212                    if (g && !f.contains(g)) f.appendChild(g);
13213                    if(d.contains(e))d.insertBefore(f, e);
13214                    varPlaced = true;
13215                }catch(_){}
13216            }
13217            if (!promoPlaced && promoBanner && compareSection && compareSection.parentNode) {
13218                try{
13219                    compareSection.parentNode.insertBefore(promoBanner, compareSection);
13220                    promoPlaced = true;
13221                }catch(_){}
13222            }
13223        } else {
13224            if (titlePlaced && titleRow && d) {
13225                try{
13226                    const vcombined = document.getElementById('pd-variant-combined');
13227                    if (vcombined && d.contains(vcombined)) {
13228                        d.insertBefore(titleRow, vcombined);
13229                    } else {
13230                        d.insertBefore(titleRow, d.firstChild);
13231                    }
13232                    titlePlaced=false;
13233                }catch(_){}
13234            }
13235            if (pricePlaced && b && c && c.parentNode) {
13236                try{c.parentNode.insertBefore(b, c.nextSibling);pricePlaced=false;}catch(_){}
13237            }
13238            if (varPlaced) {
13239                try{
13240                    if (i && j && j.parentNode && !j.parentNode.contains(i)) j.parentNode.insertBefore(i, j.nextSibling);
13241                    if (g && h && h.parentNode && !h.parentNode.contains(g)) h.parentNode.insertBefore(g, h.nextSibling);
13242                    if (f) { f.hidden = true; f.innerHTML = ''; }
13243                    varPlaced = false;
13244                }catch(_){}
13245            }
13246            if (promoPlaced && promoBanner && b) {
13247                try{
13248                    b.appendChild(promoBanner);
13249                    promoPlaced = false;
13250                }catch(_){}
13251            }
13252        }
13253    }
13254
13255    mq.addEventListener ? mq.addEventListener('change', apply) : mq.addListener(apply);
13256    apply();
13257
13258    const fab = document.getElementById('pd-mobile-fab');
13259    if (fab) {
13260        const pid = parseInt(fab.getAttribute('data-pid')||'0',10) || 325;
13261        const btnCart = document.getElementById('mf-cart');
13262
13263        if (btnCart) btnCart.addEventListener('click', function(){
13264            const varInput = document.getElementById('pd-variant-id');
13265            const variantId = varInput ? parseInt(varInput.value||'0',10) : 0;
13266            try { if (window.addToCart) addToCart(pid, 1, variantId); } catch(_){}
13267            const variantText = getVariantSummaryText();
13268            const skuText = (function(){ const el=document.getElementById('pd-sku'); return el?el.textContent.trim():PD_SKU; })();
13269            showToast({
13270                type: 'success',
13271                title: 'Berhasil ditambahkan ke Keranjang',
13272                message: `<strong>${escapeHtml(PD_NAME)}</strong>${variantText?` · <em>${escapeHtml(variantText)}</em>`:''}<br>SKU: ${escapeHtml(skuText)} · Qty: 1`,
13273                duration: 5000,
13274                actions: [
13275                    { label: 'Lihat Keranjang', onClick: ()=> { window.location.href = '/keranjang'; } },
13276                    { label: 'Lanjut Belanja', variant: 'alt', onClick: ()=>{} }
13277                ]
13278            });
13279        });
13280    }
13281})();
13282
13283/* ====== Auto-hide installment banner if sidebar too tall (Desktop only) ====== */
13284(function(){
13285    function adjustSidebarBanner() {
13286        if (window.innerWidth <= 768) return;
13287        const sidebar = document.querySelector('.pd-sidebar');
13288        const infoCard = document.querySelector('.pd-info-card');
13289        const banner = document.querySelector('.pd-side-card.inst-alt-banner.inst-alt-banner--compact');
13290        if (!sidebar || !infoCard || !banner) return;
13291        const sidebarHeight = sidebar.offsetHeight;
13292        const infoCardHeight = infoCard.offsetHeight;
13293        if (sidebarHeight > infoCardHeight) {
13294            banner.style.display = 'none';
13295        } else {
13296            banner.style.display = '';
13297        }
13298    }
13299    if (document.readyState === 'loading') {
13300        document.addEventListener('DOMContentLoaded', adjustSidebarBanner);
13301    } else {
13302        adjustSidebarBanner();
13303    }
13304    let resizeTimer;
13305    window.addEventListener('resize', function() {
13306        clearTimeout(resizeTimer);
13307        resizeTimer = setTimeout(adjustSidebarBanner, 250);
13308    });
13309    const pwpSection = document.getElementById('pwp-deals-section');
13310    if (pwpSection) {
13311        const observer = new MutationObserver(function(mutations) {
13312            let shouldRecheck = false;
13313            mutations.forEach(function(mutation) {
13314                if (mutation.type === 'attributes' && mutation.attributeName === 'hidden') {
13315                    shouldRecheck = true;
13316                } else if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
13317                    shouldRecheck = true;
13318                }
13319            });
13320            if (shouldRecheck) {
13321                setTimeout(adjustSidebarBanner, 100);
13322            }
13323        });
13324        observer.observe(pwpSection, {
13325            attributes: true,
13326            childList: true,
13327            subtree: true
13328        });
13329    }
13330    window.adjustSidebarBanner = adjustSidebarBanner;
13331})();
13332
13333/* ====== Global constants ====== */
13334const IS_DIGITAL=false;
13335const IS_PREORDER=false;
13336
13337/* ====== Variants state (REVISED) ====== */
13338(function initVariants(){
13339const _0x7g8h=[];window.variants=_0x7g8h;
13340window.isPreorderProduct=false;
13341    const attrOrder = [];
13342
13343    if (!variants.length || !attrOrder.length) return;
13344
13345    function comboKey(sel){
13346        return attrOrder.map(a => a + '=' + (sel[a] ?? '')).join('|');
13347    }
13348
13349    const variantMap = {};
13350    variants.forEach(v=>{
13351        const key = comboKey(v.attrs);
13352        variantMap[key] = v;
13353    });
13354
13355    function readSelection(){
13356        const sel = {};
13357        attrOrder.forEach(a=>{
13358            const r = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"] input[type=radio]:checked`);
13359            sel[a] = r ? r.value : '';
13360        });
13361        return sel;
13362    }
13363
13364    function findVariant(sel){
13365        return variantMap[comboKey(sel)] || null;
13366    }
13367
13368    function autoFillMissing(sel){
13369        let changed = false;
13370        for (const a of attrOrder) {
13371            if (sel[a]) continue;
13372            const possible = [];
13373            const seen = new Set();
13374            variants.forEach(v=>{
13375                let ok = true;
13376                for (const a2 of attrOrder) {
13377                    if (a2 === a) continue;
13378                    if (sel[a2] && v.attrs[a2] !== sel[a2]) { ok = false; break; }
13379                }
13380                if (ok) {
13381                    const val = v.attrs[a];
13382                    if (!seen.has(val)) { seen.add(val); possible.push(val); }
13383                }
13384            });
13385            if (possible.length === 1) {
13386                const val = possible[0];
13387                const input = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"] input[type=radio][value="${CSS.escape(val)}"]`);
13388                if (input && !input.checked) { input.checked = true; sel[a] = val; changed = true; }
13389            }
13390        }
13391        return changed;
13392    }
13393
13394    function rupiah(n){
13395        n = parseInt(n||0,10)||0;
13396        return 'Rp ' + n.toString().replace(/\B(?=(\d{3})+(?!\d))/g,'.');
13397    }
13398    
13399    function focusGalleryByUrl(imageUrl) {
13400        const isMobile = window.matchMedia && window.matchMedia('(max-width: 640px)').matches;
13401          if (isMobile) {
13402            _0xau1v(imageUrl);
13403            return;
13404          }
13405        
13406          const gallery = document.getElementById('pd-thumb-gallery');
13407          const targetName = _0x8s9t(imageUrl);
13408        
13409          if (gallery) {
13410            const btns = Array.from(gallery.querySelectorAll('.pd-thumb-btn'));
13411            const btn = btns.find(b => _0x8s9t(b.getAttribute('data-full') || '') === targetName);
13412            if (btn) { btn.click(); return; }
13413          }
13414          
13415          _0xau1v(imageUrl);
13416    }
13417    
13418
13419function formatStockLabel(n){n=parseInt(n||0,10)||0;return(n>10)?'10+':String(n)}
13420function 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(_){}}
13421
13422    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)}
13423
13424    function applyVariant(v, focusImage = true){
13425        const elCur  = document.getElementById('pd-price-current');
13426        const elOri  = document.getElementById('pd-price-original');
13427        const elDisc = document.getElementById('pd-discount-badge');
13428        const elSku  = document.getElementById('pd-sku');
13429        const elStock= document.getElementById('pd-stock-badge');
13430        const elQty  = document.getElementById('qty');
13431        const btnAdd = document.getElementById('btn-add-cart');
13432        const btnBuy = document.getElementById('btn-buy');
13433        const hidVar = document.getElementById('pd-variant-id');
13434
13435        const mfCart= document.getElementById('mf-cart');
13436        const mfBuy = document.getElementById('mf-buy');
13437        const fab = document.getElementById('pd-mobile-fab');
13438
13439        if (hidVar) hidVar.value = v ? v.id : 0;
13440        if (elCur) elCur.textContent = rupiah(v ? v.final : 0);
13441        if (elSku && v && v.sku) elSku.textContent = v.sku;
13442
13443        if (v && v.ori > v.final) {
13444            if (elOri) { elOri.style.display='inline'; elOri.textContent = rupiah(v.ori); }
13445            if (elDisc) { elDisc.style.display='inline'; elDisc.textContent = '-' + (v.disc||0) + '%'; }
13446        } else {
13447            if (elOri) elOri.style.display='none';
13448            if (elDisc) elDisc.style.display='none';
13449        }
13450
13451        const stock = v ? (parseInt(v.stock||0,10)||0) : 0;
13452        let effectiveStock = stock;
13453        let isActivePreorder = false;
13454        if (window.isPreorderProduct && v && 'preorder_remaining' in v && v.preorder_remaining !== null) {
13455            effectiveStock = parseInt(v.preorder_remaining, 10);
13456            if (isNaN(effectiveStock)) effectiveStock = 0;
13457            isActivePreorder = true;
13458        }
13459        if (elStock){
13460            elStock.classList.remove('in','out');
13461            if (effectiveStock>0){ 
13462                elStock.classList.add('in'); 
13463                elStock.textContent = IS_DIGITAL ? 'Tersedia' : (isActivePreorder ? 'Sisa Slot: ' + effectiveStock : 'Stok: ' + formatStockLabel(effectiveStock)); 
13464            } else { 
13465                elStock.classList.add('out'); 
13466                elStock.textContent = 'Habis'; 
13467            }
13468        }
13469        if (elQty){
13470            elQty.max = String(effectiveStock>0?effectiveStock:0);
13471            if (effectiveStock<=0) { elQty.value = '0'; elQty.disabled = true; }
13472            else { if (parseInt(elQty.value||'1',10) < 1) elQty.value='1'; elQty.disabled=false; }
13473        }
13474        
13475        const shouldDisable = !(v && effectiveStock>0);
13476        const updateButtons = () => {
13477            const btnAddCart = document.getElementById('btn-add-cart');
13478            const btnBuyNow = document.getElementById('btn-buy');
13479            const mobileCart = document.getElementById('mf-cart');
13480            const mobileBuy = document.getElementById('mf-buy');
13481            [btnAddCart, btnBuyNow, mobileCart, mobileBuy].forEac
13481h(btn => {
13482                if (!btn) return;
13483                if (shouldDisable) {
13484                    btn.disabled = true;
13485                    btn.setAttribute('disabled', 'disabled');
13486                    btn.classList.add('stock-disabled');
13487                    btn.setAttribute('data-stock-blocked', 'true');
13488                    btn.setAttribute('data-stock-reason', window.isPreorderProduct ? 'Pre-order habis' : 'Stok habis');
13489                } else {
13490                    btn.disabled = false;
13491                    btn.removeAttribute('disabled');
13492                    btn.classList.remove('stock-disabled');
13493                    btn.removeAttribute('data-stock-blocked');
13494                    btn.removeAttribute('data-stock-reason');
13495                }
13496            });
13497        };
13498        updateButtons();
13499        setTimeout(updateButtons, 10);
13500        setTimeout(updateButtons, 50);
13501        setTimeout(updateButtons, 100);
13502        if (mfBuy && v) {
13503            const voucherBtn = document.getElementById('btn-buy-voucher');
13504            const voucherCode = fab ? fab.dataset.voucherCode : '';
13505            let voucherDiscount = 0;
13506            
13507            if (voucherBtn && voucherCode) {
13508                const vType = voucherBtn.dataset.voucherType;
13509                const vValue = parseFloat(voucherBtn.dataset.voucherValue) || 0;
13510                const vMax = parseFloat(voucherBtn.dataset.voucherMax) || 0;
13511                const finalPrice = v.final || 0;
13512                if (vType && vValue > 0) {
13513                    voucherDiscount = calcVoucherDisc(vType, vValue, finalPrice, vMax);
13514                    if (fab) fab.dataset.voucherValue = voucherDiscount.toString();
13515                }
13516            }
13517            
13518            const finalPrice = v.final || 0;
13519            const textEl = mfBuy.querySelector('.mf-buy-text');
13520            const priceEl = mfBuy.querySelector('.mf-buy-price');
13521            
13522            if (effectiveStock > 0) {
13523                    if (IS_PREORDER) {
13524                        if (textEl) textEl.textContent = 'Pre Order Sekarang';
13525                        if (priceEl) priceEl.textContent = rupiah(finalPrice);
13526                    } else if (voucherCode && voucherDiscount > 0) {
13527                        if (textEl) textEl.textContent = 'Beli dengan Voucher';
13528                        if (priceEl) priceEl.textContent = rupiah(Math.max(0, finalPrice - voucherDiscount));
13529                    } else {
13530                        if (textEl) textEl.textContent = 'Beli Sekarang';
13531                        if (priceEl) priceEl.textContent = rupiah(finalPrice);
13532                    }
13533                } else {
13534                    if (textEl) textEl.textContent = 'Stok Habis';
13535                    if (priceEl) priceEl.textContent = rupiah(finalPrice);
13536                }
13537            }
13538        
13539        const isMobile = window.initMobile ?? 0;
13540        if (window.instSetBasePrice) window.instSetBasePrice(v ? (isMobile == '1' ? v.ori : v.final) : 0);
13541        if (window.updateSoldBySku) window.updateSoldBySku(v.sku);
13542        updateBranchPopover(v);
13543        if (v && v.image_url && focusImage) focusGalleryByUrl(v.image_url);
13544        if (v) {
13545            window.dispatchEvent(new CustomEvent('pd-variant-changed', {
13546                detail: {
13547                    price: v.final,
13548                    originalPrice: v.ori,
13549                    discount: v.disc,
13550                    stock: effectiveStock,
13551                    sku: v.sku,
13552                    variant_id: v.id
13553                }
13554            }));
13555            
13556            const encodedHash = encodeVariantHash(v.id);
13557            if (encodedHash && window.history && window.history.replaceState) {
13558                const newHash = '#v=' + encodedHash;
13559                const newUrl = window.location.pathname + window.location.search + newHash;
13560                window.history.replaceState(null, '', newUrl);
13561            }
13562        }
13563    }
13564
13565    let sel = readSelection();
13566    if (autoFillMissing(sel)) sel = readSelection();
13567
13568    let v = findVariant(sel);
13569    if (!v) {
13570        const candidates = variants.filter(variant=>{
13571            return attrOrder.every(a=>{
13572                if (!sel[a]) return true;
13573                return variant.attrs[a] === sel[a];
13574            });
13575        });
13576        if (candidates.length >= 1) {
13577            v = candidates[0];
13578            attrOrder.forEach(a=> sel[a] = v.attrs[a]);
13579            attrOrder.forEach(a=>{
13580                const val = sel[a];
13581                const input = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"] input[type=radio][value="${CSS.escape(val)}"]`);
13582                if (input) input.checked = true;
13583            });
13584        }
13585    }
13586
13587    const initSel = readSelection();
13588    const initVar = findVariant(initSel) || variants[0] || null;
13589    if (document.readyState === 'loading') {
13590        document.addEventListener('DOMContentLoaded', () => applyVariant(initVar, false));
13591    } else {
13592        applyVariant(initVar, false);
13593    }
13594    
13595function updateSummary(sel){Object.entries(sel).forEach(([a,val])=>{const el=document.getElementById('vs-'+a);if(el)el.textContent=val||'-'})}
13596    function refreshOptions(sel){
13597        attrOrder.forEach(a=>{
13598            const group = document.querySelector(`.v-group[data-attr="${CSS.escape(a)}"]`);
13599            if (!group) return;
13600            const chips = group.querySelectorAll('.v-chip');
13601            chips.forEach(ch=>{
13602                const val = ch.getAttribute('data-value');
13603                const possible = variants.some(v=>{
13604                    if (v.attrs[a] !== val) return false;
13605                    for (const a2 of attrOrder) {
13606                        if (a2 === a) continue;
13607                        if (sel[a2] && v.attrs[a2] !== sel[a2]) return false;
13608                    }
13609                    return true;
13610                });
13611                const input = ch.querySelector('input[type=radio]');
13612                const disabled = !possible;
13613                if (input) input.disabled = disabled;
13614                ch.classList.toggle('disabled', disabled);
13615
13616                let candidateVariant = null;
13617                if (possible) {
13618                    candidateVariant = variants.find(v=>{
13619                        if (v.attrs[a] !== val) return false;
13620                        for (const a2 of attrOrder) {
13621                            if (a2 === a) continue;
13622                            if (sel[a2] && v.attrs[a2] !== sel[a2]) return false;
13623                        }
13624                        return true;
13625                    });
13626                }
13627                const outOfStock = candidateVariant ? (window.isPreorderProduct && candidateVariant.preorder_remaining !== null ? candidateVariant.preorder_remaining <= 0 : candidateVariant.stock <= 0) : false;
13628                ch.classList.toggle('out', !disabled && outOfStock);
13629                const badge = ch.querySelector('.chip-badge');
13630                if (badge) badge.hidden = !(candidateVariant && outOfStock);
13631            });
13632            group.querySelectorAll('.v-chip').forEach(ch=>{
13633                const input = ch.querySelector('input[type=radio]');
13634                ch.classList.toggle('selected', input && input.checked);
13635            });
13636        });
13637    }
13638    updateSummary(sel);
13639    refreshOptions(sel);
13640    document.querySelectorAll('.v-chip').forEach(chip=>{const input=chip.querySelector('input[type=radio]');if(input&&input.checked){chip.classList.add('selected');}});
13641
13642    const container = document.getElementById('pd-variants');
13643    container && container.addEventListener('change', function(ev){
13644        const radio = ev.target.closest('input[type=radio]');
13645        if (!radio) return;
13646        sel = readSelection();
13647        if (autoFillMissing(sel)) sel = readSelection();
13648        const chosen = findVariant(sel);
13649        applyVariant(chosen || null, true);
13650        
13651        updateSummary(sel);
13652        refreshOptions(sel);
13653    });
13654    
13655window.applyVariant=applyVariant;
13656})();
13657
13658(function cleanBrokenDescImages(){
13659    const containers = [
13660        document.querySelector('.pd-full-desc-body'),
13661        document.querySelector('.pd-short-desc')
13662    ].filter(Boolean);
13663
13664    containers.forEach(root => {
13665        const imgs = root.querySelectorAll('img');
13666        imgs.forEach(img => {
13667            if (img.complete && img.naturalWidth === 0) {
13668                img.remove();
13669                return;
13670            }
13671            img.addEventListener('error', () => {
13672                img.remove();
13673            }, { once:true });
13674        });
13675    });
13676})();
13677
13678/* ====== Scroll To Top ====== */
13679(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'})})})();
13680
13681/* Format stok 10+ */
13682function _0xk67l(n){n=parseInt(n||0,10)||0;return n>10?'10+':String(n)}
13683
13684/* Toggle popover stok per cabang */
13685(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=''}}
13686function 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()}
13687a.addEventListener('click',function(h){h.preventDefault();b.classList.contains('show')?f():e()});if(c)c.addEventListener('click',f)})();
13688
13689/* ===== Share Product Modal ===== */
13690(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://norita.mx/",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
13690ncodeURIComponent(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)}})();
13691
13692/* ===== Embedding variants (server → client) ===== */
13693(function(){
13694  window.__PD_VARIANTS_WITH_BRANCH__ = [];
13695})();
13696
13697/* ===== Helper: update angka popover ===== */
13698function updateStockPopoverForVariant(v){
13699  try{
13700    const elMeg = document.getElementById('sp-qty-meg');
13701    const elMdf = document.getElementById('sp-qty-mdf');
13702    const elBda = document.getElementById('sp-qty-bda');
13703
13704    if (typeof v?.smm === 'number' && elMeg) elMeg.textContent = formatStockLabel(Math.max(0, v.smm|0));
13705    if (typeof v?.smf === 'number' && elMdf) elMdf.textContent = formatStockLabel(Math.max(0, v.smf|0));
13706    if (typeof v?.sba === 'number' && elBda) elBda.textContent = formatStockLabel(Math.max(0, v.sba|0));
13707  }catch(_){}
13708}
13709
13710/* ===== applyVariant: 10+ dan update popover ===== */
13711(function patchApplyVariant(){
13712  if (typeof applyVariant !== 'function') return;
13713  const original = applyVariant;
13714  window.applyVariant = function(v){
13715    original(v);
13716    
13717    try {
13718      const elStock = document.getElementById('pd-stock-badge');
13719      if (elStock){
13720        let txt = elStock.textContent || '';
13721        let m = txt.match(/\d+/);
13722        if (m) {
13723          const n = parseInt(m[0]||'0',10)||0;
13724          elStock.textContent = (n>0) ? ('Stok: ' + formatStockLabel(n)) : 'Habis';
13725        }
13726      }
13727    } catch(_){}
13728if(window.__PD_VARIANTS_WITH_BRANCH__&&v){const vv=window.__PD_VARIANTS_WITH_BRANCH__.find(x=>x.id===v.id)||v;updateStockPopoverForVariant(vv)}
13729  };
13730})();
13731
13732(function initCurrentVariantBranch(){
13733  const list = window.__PD_VARIANTS_WITH_BRANCH__ || [];
13734  if (!list.length) return;
13735  const hid = document.getElementById('pd-variant-id');
13736  const vid = hid ? parseInt(hid.value||'0',10) : 0;
13737  const current = list.find(x => x.id === vid) || list[0] || null;
13738  if (current) updateStockPopoverForVariant(current);
13739})();
13740
13741(function(){
13742    if (!window.ga4Ecomm) return;
13743    const catSegs = ["Komputer & Laptop","Laptop","Gaming"];
13744    window.ga4Ecomm.viewItem({
13745      currency: 'IDR',
13746      value: 20789000,
13747      item_id: "SB0B3T",
13748      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",
13749      item_brand: "Lenovo ",
13750      item_category: catSegs && catSegs.length ? catSegs.join(' / ') : ''
13751    });
13752  })();
13753  
13754(function patchApplyVariantFocus(){
13755  if (typeof applyVariant !== 'function') return;
13756  const original = applyVariant;
13757  window.applyVariant = function(v, focusImage = true){
13758    
13759    original(v, focusImage);
13760    
13761    if (focusImage && v && v.image_url) {
13762      _0xau1v(v.image_url);
13763    }
13764  };
13765})();
13766
13767(function() {
13768  var soldMap = [];
13769  var soldTotalLabel = "Terjual 2";
13770
13771  function formatSoldLabel(qty) {
13772    if (qty >= 1000000) {
13773      var v = Math.floor((qty / 100000) ) / 10;
13774      return 'Terjual ' + (String(v).replace('.', ',')) + 'jt+';
13775    }
13776    if (qty >= 1000) {
13777      var v2 = Math.floor((qty / 100) ) / 10;
13778      return 'Terjual ' + (String(v2).replace('.', ',')) + 'rb+';
13779    }
13780    if (qty >= 100) {
13781      var v2 = Math.floor((qty / 100) ) * 100;
13782      return 'Terjual ' + (String(v2).replace('.', ',')) + '+';
13783    }
13784    if (qty >= 10) {
13785      var v2 = Math.floor((qty / 10) ) * 10;
13786      return 'Terjual ' + (String(v2).replace('.', ',')) + '+';
13787    }
13788    return 'Terjual ' + (qty.toLocaleString('id-ID'));
13789  }
13790
13791  function updateSoldLabel(labelText) {
13792    var el = document.querySelector('#pd-sold-inline, [data-role="sold-chip"]');
13793    if (el) el.textContent = labelText;
13794  }
13795
13796  window.updateSoldBySku = function(sku) {
13797    if (!sku || typeof sku !== 'string') {
13798      updateSoldLabel(soldTotalLabel);
13799      return;
13800    }
13801    var qty = soldMap[sku] || 0;
13802    updateSoldLabel(formatSoldLabel(qty));
13803  };
13804
13805    updateSoldLabel(soldTotalLabel);
13806  })();
13807
13808(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)}})}
13809function 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}
13810const 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}});
13810function formatRupiah(n){return'Rp'+Math.floor(n).toLocaleString('id-ID')}})();
13811
13812(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')}})();
13813</script>
vendor: 2 bytes, line 13813
13813
13814<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
13815window.PD_ID=325;window.PD_NAME=_0x3c4d;window.PD_SKU=_0x5e6f;window.PD_CATEGORY_PATH="Komputer & Laptop/Laptop/Gaming";window.PD_BEST_VOUCHER=null;
13816window.IS_DIGITAL=IS_DIGITAL;
13817window.isPreorderProduct=false;
13818window.PD_PREORDER_DATA=null;
13819</script>
13819
13820
13821<script data-inlined="pd-lightbox.js">
13822// PlazaIT Product Lightbox (vanilla, no CDN) — v4 (swipe-down to close)
13823(function(){
13824  const state = {
13825    items: [],
13826    open: false,
13827    idx: 0,
13828    scale: 1,
13829    minScale: 1,
13830    maxScale: 4,
13831    tx: 0, ty: 0,
13832    isDragging: false,
13833    dragStartX: 0, dragStartY: 0,
13834    baseTx: 0, baseTy: 0,
13835    touchStartX: 0, touchStartY: 0,
13836    lastTap: 0,
13837    swipeMode: 'none', // 'none' | 'h' | 'v'
13838    totalDx: 0, totalDy: 0
13839  };
13840
13841  let root, imgEl, counterEl, btnPrev, btnNext, btnClose, canvas;
13842
13843  const ORIGIN = (location.origin || (location.protocol + '//' + location.host));
13844
13845  function absUrl(u){
13846    if (!u) return '';
13847    try {
13848      if (/^https?:\/\//i.test(u)) return u;
13849      return new URL(u, ORIGIN).href;
13850    } catch(_) { return u; }
13851  }
13852  function fileName(u){
13853    try { return new URL(u, ORIGIN).pathname.split('/').pop().toLowerCase(); }
13854    catch(_){ return String(u||'').split('/').pop().toLowerCase(); }
13855  }
13856
13857  function buildItems(){
13858    const gd = (window.galleryData || []).filter(Boolean);
13859    if (gd.length){
13860      state.items = gd.map(g => ({
13861        src: absUrl(g.url), alt: g.alt || '', slug: g.slug || '', w: g.w || 1200, h: g.h || 1200
13862      })).filter(it => !!it.src);
13863      return;
13864    }
13865    const thumbs = document.querySelectorAll('.pd-thumb-btn');
13866    state.items = Array.from(thumbs).map(btn => {
13867      const raw = btn.getAttribute('data-full') || '';
13868      return { src: absUrl(raw), alt: btn.getAttribute('data-alt') || '', slug: btn.getAttribute('data-slug') || '', w: 1200, h: 1200 };
13869    }).filter(it => !!it.src);
13870  }
13871
13872  function ensureUI(){
13873    if (root) return;
13874    root = document.createElement('div');
13875    root.className = 'pd-lb';
13876    root.id = 'pd-lightbox';
13877    root.setAttribute('role','dialog');
13878    root.setAttribute('aria-modal','true');
13879    root.setAttribute('aria-hidden','true');
13880    root.setAttribute('tabindex','-1');
13881
13882    // Canvas terlebih dulu (z-index di CSS membuat tombol di atas)
13883    root.innerHTML = `
13884      <div class="pd-lb-canvas"><img class="pd-lb-img" id="pd-lb-img" alt=""></div>
13885      <button class="pd-lb-close" aria-label="Tutup" title="Tutup">&times;</button>
13886      <button class="pd-lb-prev" aria-label="Sebelumnya" title="Sebelumnya">&#10094;</button>
13887      <button class="pd-lb-next" aria-label="Berikutnya" title="Berikutnya">&#10095;</button>
13888      <div class="pd-lb-counter" id="pd-lb-counter">1 / 1</div>
13889    `;
13890    document.body.appendChild(root);
13891
13892    imgEl     = root.querySelector('#pd-lb-img');
13893    counterEl = root.querySelector('#pd-lb-counter');
13894    btnPrev   = root.querySelector('.pd-lb-prev');
13895    btnNext   = root.querySelector('.pd-lb-next');
13896    btnClose  = root.querySelector('.pd-lb-close');
13897    canvas    = root.querySelector('.pd-lb-canvas');
13898
13899    // Button clicks
13900    btnPrev.addEventListener('click', (e)=>{ e.stopPropagation(); prev(); });
13901    btnNext.addEventListener('click', (e)=>{ e.stopPropagation(); next(); });
13902    btnClose.addEventListener('click', (e)=>{ e.stopPropagation(); close(); });
13903
13904    // Klik backdrop untuk tutup
13905    root.addEventListener('click', (e)=>{ if (e.target === root) close(); });
13906
13907    // Keyboard global
13908    document.addEventListener('keydown', (e)=>{
13909      if (!state.open) return;
13910      if (e.key === 'Escape') { e.preventDefault(); close(); }
13911      if (e.key === 'ArrowRight') { e.preventDefault(); next(); }
13912      if (e.key === 'ArrowLeft')  { e.preventDefault(); prev(); }
13913    });
13914
13915    // Wheel zoom (desktop)
13916    root.addEventListener('wheel', (e)=>{
13917      if (!state.open) return;
13918      e.preventDefault();
13919      const delta = -Math.sign(e.deltaY) * 0.2;
13920      zoomAt(delta, e.clientX, e.clientY);
13921    }, { passive: false });
13922
13923    // Drag pan (mouse)
13924    canvas.addEventListener('mousedown', (e)=>{
13925      if (state.scale <= 1) return;
13926      state.isDragging = true;
13927      state.dragStartX = e.clientX;
13928      state.dragStartY = e.clientY;
13929      state.baseTx = state.tx;
13930      state.baseTy = state.ty;
13931      e.preventDefault();
13932    });
13933    window.addEventListener('mousemove', (e)=>{
13934      if (!state.isDragging) return;
13935      const dx = e.clientX - state.dragStartX;
13936      const dy = e.clientY - state.dragStartY;
13937      setTranslate(state.baseTx + dx, state.baseTy + dy);
13938    });
13939    window.addEventListener('mouseup', ()=>{ state.isDragging = false; });
13940
13941    // Touch: swipe navigate (H), swipe-down close (V), double-tap zoom, drag pan
13942    canvas.addEventListener('touchstart', (e)=>{
13943      if (!state.open) return;
13944      if (e.touches.length === 1) {
13945        const t = e.touches[0];
13946        state.touchStartX = t.clientX;
13947        state.touchStartY = t.clientY;
13948        state.totalDx = 0;
13949        state.totalDy = 0;
13950        state.swipeMode = 'none';
13951
13952        state.isDragging = state.scale > 1;
13953        state.dragStartX = t.clientX;
13954        state.dragStartY = t.clientY;
13955        state.baseTx = state.tx;
13956        state.baseTy = state.ty;
13957
13958        // Double-tap toggle zoom
13959        const now = Date.now();
13960        if (now - state.lastTap < 300) {
13961          if (state.scale > 1) resetTransform(); else zoomAt(+1, t.clientX, t.clientY, 2);
13962          state.lastTap = 0;
13963          e.preventDefault();
13964          return;
13965        }
13966        state.lastTap = now;
13967      }
13968    }, { passive: true });
13969
13970    canvas.addEventListener('touchmove', (e)=>{
13971      if (!state.open) return;
13972
13973      // Drag pan saat zoom
13974      if (state.isDragging && e.touches.length === 1 && state.scale > 1) {
13975        e.preventDefault();
13976        const t = e.touches[0];
13977        const dx = t.clientX - state.dragStartX;
13978        const dy = t.clientY - state.dragStartY;
13979        setTranslate(state.baseTx + dx, state.baseTy + dy);
13980        return;
13981      }
13982
13983      // Gesture saat scale <= 1
13984      if (state.scale <= 1 && e.touches.length === 1) {
13985        const t = e.touches[0];
13986        const dx = t.clientX - state.touchStartX;
13987        const dy = t.clientY - state.touchStartY;
13988        state.totalDx = dx; state.totalDy = dy;
13989
13990        // Tentukan mode swipe di awal gerakan
13991        if (state.swipeMode === 'none') {
13992          const absX = Math.abs(dx), absY = Math.abs(dy);
13993          if (absY > 12 && absY > absX * 1.3) {
13994            state.swipeMode = 'v'; // swipe vertical (untuk close)
13995          } else if (absX > 12 && absX > absY * 1.3) {
13996            state.swipeMode = 'h'; // swipe horizontal (ganti slide)
13997          }
13998        }
13999
14000        if (state.swipeMode === 'v') {
14001          // Ikuti jari, kurangi opacity untuk feedback
14002          e.preventDefault();
14003          setRootDrag(dy);
14004        } else if (state.swipeMode === 'h') {
14005          // Biarkan sampai touchend untuk menentukan next/prev (tidak perlu preventDefault di sini)
14006        }
14007      }
14008    }, { passive: false });
14009
14010    canvas.addEventListener('touchend', (e)=>{
14011      if (!state.open) return;
14012
14013      if (state.scale <= 1) {
14014        const dx = state.totalDx;
14015        const dy = state.totalDy;
14016
14017        if (state.swipeMode === 'v') {
14018          const TH = 80; // threshold jarak piksel untuk menutup
14019          if (Math.abs(dy) > TH) {
14020            // Animasi keluar arah swipe
14021            animateClose(dy);
14022          } else {
14023            // Snap back
14024            resetRootDrag(true);
14025          }
14026        } else if (state.swipeMode === 'h') {
14027          if (Math.abs(dx) > 50) { dx < 0 ? next() : prev(); }
14028        }
14029      }
14030
14031      state.isDragging = false;
14032      state.swipeMode = 'none';
14033      state.totalDx = 0;
14034      state.totalDy = 0;
14035    });
14036  }
14037
14038  function setImage(idx){
14039    if (!state.items.length) return;
14040    state.idx = (idx + state.items.length) % state.items.length;
14041    const it = state.items[state.idx];
14042    imgEl.src = it.src;
14043    imgEl.alt = it.alt || '';
14044    counterEl.textContent = (state.idx + 1) + ' / ' + state.items.length;
14045    resetTransform();
14046    preloadNeighbors();
14047  }
14048
14049  function resetTransform(){
14050    state.scale = 1;
14051    state.tx = 0; state.ty = 0;
14052    applyTransform();
14053  }
14054
14055  function setTranslate(tx, ty){
14056    state.tx = tx; state.ty = ty;
14057    applyTransform();
14058  }
14059
14060  function applyTransform(){
14061    imgEl.style.transform = `translate3d(${state.tx}px, ${state.ty}px, 0) scale(${state.scale})`;
14062  }
14063
14064  function zoomAt(delta, clientX, clientY, snapTo){
14065    const old = state.scale;
14066    let next = (typeof snapTo === 'number') ? snapTo : (old + delta);
14067    next = Math.max(state.minScale, Math.min(state.maxScale, next));
14068
14069    if (next !== old) {
14070      const rect = imgEl.getBoundingClientRect();
14071      const cx = clientX - rect.left - rect.width/2;
14072      const cy = clientY - rect.top  - rect.height/2;
14073      state.tx -= cx * (next/old - 1);
14074      state.ty -= cy * (next/old - 1);
14075      state.scale = next;
14076      applyTransform();
14077    }
14078  }
14079
14080  function preloadNeighbors(){
14081    if (state.items.length < 2) return;
14082    const nextIdx = (state.idx + 1) % state.items.length;
14083    const prevIdx = (state.idx - 1 + state.items.length) % state.items.length;
14084    [nextIdx, prevIdx].forEach(i=>{
14085      const img = new Image(); img.src = state.items[i].src;
14086    });
14087  }
14088
14089  // ----- Swipe-down visual helpers -----
14090  function setRootDrag(dy){
14091    const damped = dy * 0.6; // redam gerakan agar lembut
14092    const abs = Math.abs(dy);
14093    const fade = Math.max(0, Math.min(1, 1 - abs / 300)); // transparansi sesuai jarak
14094    root.style.transform = `translate3d(0, ${damped}px, 0)`;
14095    root.style.opacity = String(0.95 * fade + 0.05); // jangan jadi 0 total sebelum close
14096  }
14097  function resetRootDrag(withAnim){
14098    if (withAnim) {
14099      root.style.transition = 'transform .18s ease, opacity .18s ease';
14100      root.style.transform = 'translate3d(0,0,0)';
14101      root.style.opacity = '1';
14102      setTimeout(()=>{ root.style.transition = ''; }, 190);
14103    } else {
14104      root.style.transition = '';
14105      root.style.transform = '';
14106      root.style.opacity = '';
14107    }
14108  }
14109  function animateClose(dy){
14110    root.style.transition = 'transform .18s ease, opacity .18s ease';
14111    const off = (dy > 0 ? window.innerHeight : -window.innerHeight);
14112    root.style.transform = `translate3d(0, ${off}px, 0)`;
14113    root.style.opacity = '0';
14114    setTimeout(()=>{ root.style.transition = ''; close(); }, 180);
14115  }
14116
14117  function setSiblingsInert(makeInert){
14118    // Inert semua saudara root (elemen lain di body) agar fokus tidak lari
14119    const nodes = Array.from(document.body.children).filter(el => el !== root);
14120    nodes.forEach(el=>{
14121      if (makeInert) {
14122        if (!el.hasAttribute('inert')) el.setAttribute('inert','');
14123      } else {
14124        if (el.hasAttribute('inert')) el.removeAttribute('inert');
14125      }
14126    });
14127  }
14128
14129  function open(idx){
14130    if (!state.items.length) buildItems();
14131    if (!state.items.length) return;
14132    ensureUI();
14133
14134    // Reset efek drag visual
14135    resetRootDrag(false);
14136
14137    document.body.style.overflow = 'hidden';
14138    root.classList.add('show');
14139    root.removeAttribute('aria-hidden'); // hindari aria-hidden saat fokus child
14140    setSiblingsInert(true);
14141
14142    state.open = true;
14143    setImage(typeof idx === 'number' ? idx : 0);
14144
14145    // Fokuskan dialog agar keyboard pasti tertangkap
14146    try { root.focus(); } catch(_){}
14147  }
14148
14149  function close(){
14150    state.open = false;
14151    root.classList.remove('show');
14152    root.setAttribute('aria-hidden','true');
14153    setSiblingsInert(false);
14154    document.body.style.overflow = '';
14155
14156    // Bersihkan transform/opacity
14157    resetRootDrag(false);
14158  }
14159
14160  function next(){ setImage(state.idx + 1); }
14161  function prev(){ setImage(state.idx - 1); }
14162
14163  function findIndexByUrl(url){
14164    const target = fileName(absUrl(url));
14165    const idx = state.items.findIndex(it => fileName(it.src) === target);
14166    return idx >= 0 ? idx : 0;
14167  }
14168
14169  function initPdLightbox(){
14170    buildItems();
14171    ensureUI();
14172
14173    // Klik gambar utama → buka LB di index yang sama
14174    const mainImg = document.getElementById('pd-main-img');
14175    if (mainImg) {
14176      mainImg.style.cursor = 'zoom-in';
14177      mainImg.addEventListener('click', ()=>{
14178        open(findIndexByUrl(mainImg.src));
14179      });
14180    }
14181
14182    // Klik gambar slider mobile → buka LB di gambar yang diketuk
14183    const slider = document.getElementById('pd-mobile-slider');
14184    if (slider) {
14185      slider.addEventListener('click', (e)=>{
14186        const im = e.target && e.target.tagName === 'IMG' ? e.target : e.target.closest('img');
14187        if (!im) return;
14188        open(findIndexByUrl(im.src));
14189      });
14190    }
14191
14192    // (Opsional) double-click pada thumbnail untuk buka langsung
14193    const thumbs = document.querySelectorAll('.pd-thumb-btn');
14194    thumbs.forEach((btn)=>{
14195      let last = 0;
14196      btn.addEventListener('click', ()=>{
14197        const now = Date.now();
14198        if (now - last < 300) {
14199          const src = btn.getAttribute('data-full') || '';
14200          open(findIndexByUrl(absUrl(src)));
14201          last = 0;
14202        } else {
14203          last = now;
14204        }
14205      });
14206    });
14207
14208    // Ekspos API
14209    window.PDLightbox = { open, close, next, prev, findIndexByUrl };
14210  }
14211
14212  if (document.readyState === 'loading') {
14213    document.addEventListener('DOMContentLoaded', initPdLightbox);
14214  } else {
14215    initPdLightbox();
14216  }
14217})();
14218</script>
vendor: 2 bytes, line 14218
14218
14219<script data-inlined="cart.js">
14220(function(){
14221  function $(s,ctx=document){ return ctx.querySelector(s); }
14222  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
14223  
14224  // CSRF token helper
14225  function getCsrfToken() {
14226    const metaTag = document.querySelector('meta[name="csrf-token"]');
14227    return metaTag ? metaTag.getAttribute('content') : '';
14228  }
14229  
14230  function guessPdMeta(){
14231    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : ($('#pd-title')?.textContent || '');
14232    const sku  = (function(){ const el=$('#pd-sku'); return el ? el.textContent.trim() : (window.PD_SKU||''); })();
14233    // SECURITY FIX: Remove price from client-side metadata (will be validated server-side)
14234    const imgEl = $('#pd-main-img');
14235    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
14236    return { name, sku, image_url }; // price removed for security
14237  }
14238
14239  async function addToCart(productId, qty=1, variantId=0, metaOpt){
14240    const meta = Object.assign(guessPdMeta(), metaOpt||{});
14241    const payload = {
14242      product_id: parseInt(productId||0,10) || 0,
14243      qty: parseInt(qty||1,10) || 1,
14244      variant_id: parseInt(variantId||0,10) || 0,
14245      meta
14246    };
14247    
14248    // Check if user is logged in
14249    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
14250                      document.body.classList.contains('logged-in') ||
14251                      window.userLoggedIn === true ||
14252                      document.body.dataset.userLoggedIn === 'true';
14253    
14254    // Skip localStorage untuk logged user - mereka pakai database
14255    if (!isLoggedIn && window.guestCart) {
14256      try {
14257        window.guestCart.add(payload.product_id, payload.qty, payload.variant_id, meta);
14258      } catch (e) {
14259        console.warn('Failed to add to localStorage cart:', e);
14260      }
14261    }
14262    
14263    const res = await fetch('/api/cart/add.php', {
14264      method: 'POST',
14265      headers: { 
14266        'Content-Type':'application/json',
14267        'X-CSRF-Token': getCsrfToken()
14268      },
14269      credentials: 'same-origin',
14270      body: JSON.stringify({
14271        ...payload,
14272        csrf_token: getCsrfToken()
14273      }),
14274    });
14275    const data = await res.json().catch(()=>({ ok:false, error:'Gagal menambahkan ke keranjang' }));
14276    if (!res.ok || !data.ok) {
14277      const msg = data && data.error ? data.error : 'Gagal menambahkan ke keranjang';
14278      throw new Error(msg);
14279    }
14280
14281    // Update badge di navbar dengan data dari response
14282    try {
14283      const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
14284      if (el && data.cart && typeof data.cart.items_count === 'number') {
14285        el.textContent = String(data.cart.items_count);
14286        el.classList.toggle('is-empty', data.cart.items_count <= 0);
14287      }
14288    } catch(_){}
14289
14290    // Siarkan event global (dipakai cart-badge.js dan halaman lain)
14291    try {
14292      document.dispatchEvent(new CustomEvent('cart:updated', { detail: data.cart || {} }));
14293    } catch(_){}
14294
14295    // Update CartBadge dengan data dari response (lebih reliable)
14296    try { 
14297      if (window.CartBadge && window.CartBadge.set) {
14298        window.CartBadge.set(data.cart.items_count||0);
14299      }
14300    } catch(_){}
14301
14302    // Tambahan: refresh cart counter setelah delay kecil untuk memastikan data tersinkronisasi
14303    setTimeout(() => {
14304      if (window.updateCartCounter) {
14305        window.updateCartCounter();
14306      }
14307    }, 100);
14308
14309    return data;
14310  }
14311
14312  // Function to update cart counter from server
14313  async function updateCartCounter(){
14314    try {
14315      const res = await fetch('/api/cart/get.php', { credentials:'same-origin' });
14316      const data = await res.json().catch(()=>({ok:false}));
14317      if (data.ok && typeof data.items_count === 'number') {
14318        const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
14319        if (el) {
14320          el.textContent = String(data.items_count);
14321          el.classList.toggle('is-empty', data.items_count <= 0);
14322        }
14323        // Dispatch event
14324        try {
14325          document.dispatchEvent(new CustomEvent('cart:updated', { detail: { items_count: data.items_count } }));
14326        } catch(_){}
14327      }
14328    } catch (err) {
14329      console.log('Could not update cart counter:', err);
14330    }
14331  }
14332
14333  // Function to manually merge guest cart (if needed)
14334  async function mergeGuestCart(){
14335    try {
14336      const res = await fetch('/api/cart/merge-guest.php', {
14337        method: 'POST',
14338        headers: { 
14339          'Content-Type': 'application/json',
14340          'X-CSRF-Token': getCsrfToken()
14341        },
14342        credentials: 'same-origin',
14343        body: JSON.stringify({ csrf_token: getCsrfToken() })
14344      });
14345      const data = await res.json().catch(()=>({ok:false}));
14346      
14347      if (data.ok && data.merged_items > 0) {
14348        console.log(`Guest cart merged: ${data.merged_items} items`);
14349        // Update cart counter after merge
14350        await updateCartCounter();
14351        return data;
14352      }
14353      return data;
14354    } catch (err) {
14355      console.log('Could not merge guest cart:', err);
14356      return { ok: false, error: err.message };
14357    }
14358  }
14359
14360  window.addToCart = addToCart;
14361  window.updateCartCounter = updateCartCounter;
14362  window.mergeGuestCart = mergeGuestCart;
14363
14364  // Optional: auto-bind untuk tombol apapun yang punya data-addtocart
14365  document.addEventListener('click', async (e)=>{
14366    const btn = e.target.closest('[data-addtocart]');
14367    if (!btn) return;
14368    e.preventDefault();
14369    if (btn.disabled) return;
14370
14371    const pid = parseInt(btn.getAttribute('data-pid')||'0',10);
14372    const vid = parseInt(btn.getAttribute('data-vid')||'0',10);
14373    const qty = parseInt(btn.getAttribute('data-qty')||'1',10) || 1;
14374
14375    try {
14376      const data = await addToCart(pid, qty, vid);
14377      if (window.showToast) {
14378        const meta = guessPdMeta();
14379        window.showToast({
14380          type:'success',
14381          title:'Berhasil ditambahkan',
14382          message:`<strong>${esc(meta.name)}</strong>${meta.sku?` · <em>SKU: ${esc(meta.sku)}</em>`:''}<br>Qty: ${qty}`,
14383          duration: 3800,
14384          actions: [
14385            { label:'Lihat Keranjang', onClick: ()=> { window.location.href='/keranjang'; } },
14386            { label:'Lanjut Belanja', variant:'alt', onClick: ()=>{} }
14387          ]
14388        });
14389      }
14390    } catch (err) {
14391      if (window.showToast) {
14392        window.showToast({ type:'warn', title:'Gagal', message: String(err?.message||'Gagal menambahkan'), duration: 4200 });
14393      } else {
14394        console.error(err);
14395      }
14396    }
14397  });
14398})();
14399</script>
vendor: 2 bytes, line 14399
14399
14400<script data-inlined="stock-validator-vanilla.js">
14401/**
14402 * Stock Validation - Vanilla JS Version
14403 * 
14404 * This script provides client-side stock validation without jQuery dependency
14405 * - Works with native JavaScript and fetch API
14406 * - Graceful fallback for older browsers
14407 * - Same functionality as jQuery version
14408 */
14409
14410window.StockValidator = (function() {
14411    'use strict';
14412
14413    const config = {
14414        apiEndpoint: '/api/stock/check.php',
14415        modalSelector: '#stock-modal-overlay',  // We create our own modal
14416        loadingClass: 'loading-stock',
14417        disabledClass: 'stock-disabled',
14418        debug: true  // Enable debug logging
14419    };
14420
14421    /**
14422     * Cross-browser event listener helper
14423     */
14424    function addEventListener(element, event, handler) {
14425        if (element.addEventListener) {
14426            element.addEventListener(event, handler);
14427        } else if (element.attachEvent) {
14428            element.attachEvent('on' + event, handler);
14429        }
14430    }
14431
14432    /**
14433     * Get stock information from embedded variant data (instant, no AJAX needed)
14434     */
14435    function getStockFromEmbeddedData(variantId) {
14436        // Check if embedded variant data exists
14437        if (!window.__PD_VARIANTS_WITH_BRANCH__ || !Array.isArray(window.__PD_VARIANTS_WITH_BRANCH__)) {
14438            return null;
14439        }
14440        
14441        const variants = window.__PD_VARIANTS_WITH_BRANCH__;
14442        
14443        // SPECIAL CASE: Simple product (no variants) - empty array means simple product
14444        if (variants.length === 0) {
14445            // For simple products, we cannot determine stock from embedded data
14446            // Return null to force API validation
14447            return null;
14448        }
14449        
14450        // If no variant specified, check base product stock (all variants)
14451        if (!variantId || variantId <= 0) {
14452            // For base product, check if ANY variant has stock
14453            const isPreorder = window.isPreorderProduct || false;
14454            let hasAnyStock = false;
14455            let totalStock = 0;
14456            
14457            if (isPreorder) {
14458                // For pre-order, check preorder_remaining
14459                hasAnyStock = variants.some(v => {
14460                    if ('preorder_remaining' in v && v.preorder_remaining !== null) {
14461                        return v.preorder_remaining > 0;
14462                    }
14463                    return v.stock > 0;
14464                });
14465                totalStock = variants.reduce((sum, v) => {
14466                    if ('preorder_remaining' in v && v.preorder_remaining !== null) {
14467                        return sum + (v.preorder_remaining || 0);
14468                    }
14469                    return sum + (v.stock || 0);
14470                }, 0);
14471            } else {
14472                hasAnyStock = variants.some(v => v.stock > 0);
14473                totalStock = variants.reduce((sum, v) => sum + (v.stock || 0), 0);
14474            }
14475            
14476            return {
14477                hasStock: hasAnyStock,
14478                stock: totalStock,
14479                sku: 'Base Product',
14480                variant: null
14481            };
14482        }
14483        
14484        // Find specific variant
14485        const variant = variants.find(v => v.id === variantId);
14486        
14487        if (!variant) {
14488            return {
14489                hasStock: false,
14490                stock: 0,
14491                sku: 'Unknown Variant',
14492                variant: null
14493            };
14494        }
14495        
14496        // For pre-order products, check preorder_remaining instead of stock
14497        const isPreorder = window.isPreorderProduct || false;
14498        let hasStock = false;
14499        let availableStock = 0;
14500        
14501        if (isPreorder && 'preorder_remaining' in variant && variant.preorder_remaining !== null) {
14502            availableStock = parseInt(variant.preorder_remaining, 10);
14503            if (isNaN(availableStock)) availableStock = 0;
14504            hasStock = availableStock > 0;
14505        } else {
14506            availableStock = variant.stock || 0;
14507            hasStock = availableStock > 0;
14508        }
14509        
14510        return {
14511            hasStock: hasStock,
14512            stock: availableStock,
14513            sku: variant.sku,
14514            variant: variant
14515        };
14516    }
14517
14518    /**
14519     * Cross-browser AJAX helper
14520     */
14521    function makeRequest(url, options) {
14522        return new Promise((resolve, reject) => {
14523            // Try fetch first (modern browsers)
14524            if (typeof fetch !== 'undefined') {
14525                const fetchOptions = {
14526                    method: options.method || 'POST',
14527                    headers: {
14528                        'Content-Type': 'application/x-www-form-urlencoded',
14529                    },
14530                    body: options.data ? new URLSearchParams(options.data).toString() : null
14531                };
14532
14533                fetch(url, fetchOptions)
14534                    .then(response => {
14535                        if (!response.ok) {
14536                            throw new Error(`HTTP ${response.status}`);
14537                        }
14538                        return response.json();
14539                    })
14540                    .then(resolve)
14541                    .catch(reject);
14542            } else {
14543                // Fallback to XMLHttpRequest
14544                const xhr = new XMLHttpRequest();
14545                xhr.open(options.method || 'POST', url);
14546                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
14547                
14548                xhr.onload = function() {
14549                    if (xhr.status >= 200 && xhr.status < 300) {
14550                        try {
14551                            resolve(JSON.parse(xhr.responseText));
14552                        } catch (e) {
14553                            reject(new Error('Invalid JSON response'));
14554                        }
14555                    } else {
14556                        reject(new Error(`HTTP ${xhr.status}`));
14557                    }
14558                };
14559                
14560                xhr.onerror = function() {
14561                    reject(new Error('Network error'));
14562                };
14563                
14564                const data = options.data ? new URLSearchParams(options.data).toString() : null;
14565                xhr.send(data);
14566            }
14567        });
14568    }
14569
14570    /**
14571     * Check stock via AJAX
14572     */
14573    function checkStock(productId, variantId, qty) {
14574        return makeRequest(config.apiEndpoint, {
14575            method: 'POST',
14576            data: {
14577                product_id: productId,
14578                variant_id: variantId || 0,
14579                qty: qty || 1
14580            }
14581        });
14582    }
14583
14584    /**
14585     * Create and show stock alert modal using modal.css structure
14586     */
14587    function showStockModal(stockInfo, action = 'add to cart') {
14588        let title, message, type, iconClass;
14589
14590        // Handle both string and object formats
14591        if (typeof stockInfo === 'string') {
14592            title = 'Stok Tidak Tersedia';
14593            message = stockInfo;
14594            type = 'warning';
14595            iconClass = 'warning';
14596        } else if (stockInfo && typeof stockInfo === 'object') {
14597            if (!stockInfo.is_active) {
14598                title = 'Produk Tidak Tersedia';
14599                message = 'Maaf, produk ini sudah tidak aktif atau telah dihapus dari katalog.';
14600                type = 'danger';
14601                iconClass = 'danger';
14602            } else if (!stockInfo.is_in_stock) {
14603                title = 'Stok Habis';
14604                message = `<strong>${stockInfo.product_name || 'Produk ini'}</strong> sedang habis stok. Silakan pilih varian lain atau tunggu restock.`;
14605                if (stockInfo.sku) {
14606                    message += `<br><small style="color: #64748b;">SKU: ${stockInfo.sku}</small>`;
14607                }
14608                type = 'warning';
14609                iconClass = 'warning';
14610            } else if (!stockInfo.can_add_to_cart) {
14611                title = 'Stok Tidak Mencukupi';
14612                message = `Stok <strong>${stockInfo.product_name || 'produk'}</strong> tidak mencukupi.<br>`;
14613                message += `Tersedia: <strong>${stockInfo.available_stock}</strong>, diminta: <strong>${stockInfo.requested_qty}</strong><br>`;
14614                message += `Silakan kurangi jumlah atau pilih varian lain.`;
14615                type = 'warning';
14616                iconClass = 'warning';
14617            } else {
14618                title = 'Stok Tidak Tersedia';
14619                message = stockInfo.message || 'Produk ini tidak dapat ditambahkan ke keranjang saat ini.';
14620                type = 'warning';
14621                iconClass = 'warning';
14622            }
14623        } else {
14624            title = 'Stok Tidak Tersedia';
14625            message = 'Produk ini tidak dapat ditambahkan ke keranjang saat ini.';
14626            type = 'warning';
14627            iconClass = 'warning';
14628        }
14629
14630        // Create modal HTML using modal.css structure
14631        const modalHTML = `
14632            <div class="modal-overlay" id="stock-modal-overlay">
14633                <div class="modal-dialog">
14634                    <div class="modal-header">
14635                        <div class="modal-icon ${iconClass}">
14636                            ${getIconSVG(iconClass)}
14637                        </div>
14638                        <h3 class="modal-title">${title}</h3>
14639                    </div>
14640                    <div class="modal-body">
14641                        <p class="modal-message">${message}</p>
14642                        <div class="modal-actions">
14643                            <button type="button" class="modal-btn modal-btn-secondary" data-action="close">
14644                                Tutup
14645                            </button>
14646                            <button type="button" class="modal-btn modal-btn-primary" data-action="reload">
14647                                Muat Ulang
14648                            </button>
14649                        </div>
14650                    </div>
14651                </div>
14652            </div>
14653        `;
14654
14655        // Remove existing modal if any
14656        const existingModal = document.getElementById('stock-modal-overlay');
14657        if (existingModal) {
14658            existingModal.remove();
14659        }
14660
14661        // Add modal to body
14662        document.body.insertAdjacentHTML('beforeend', modalHTML);
14663        
14664        // Show modal with animation
14665        const modal = document.getElementById('stock-modal-overlay');
14666        setTimeout(() => {
14667            modal.classList.add('show');
14668        }, 10);
14669
14670        // Add event listeners to modal buttons
14671        const closeBtn = modal.querySelector('[data-action="close"]');
14672        const reloadBtn = modal.querySelector('[data-action="reload"]');
14673        
14674        if (closeBtn) {
14675            addEventListener(closeBtn, 'click', closeStockModal);
14676        }
14677        
14678        if (reloadBtn) {
14679            addEventListener(reloadBtn, 'click', function() {
14680                closeStockModal();
14681                window.location.reload();
14682            });
14683        }
14684
14685        // Close modal when clicking overlay
14686        addEventListener(modal, 'click', function(event) {
14687            if (event.target === modal) {
14688                closeStockModal();
14689            }
14690        });
14691
14692        // Auto-close after 8 seconds
14693        setTimeout(() => {
14694            closeStockModal();
14695        }, 8000);
14696    }
14697
14698    /**
14699     * Get icon SVG based on type
14700     */
14701    function getIconSVG(type) {
14702        const icons = {
14703            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>',
14704            warning: '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
14704<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>',
14705            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>',
14706            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>'
14707        };
14708        return icons[type] || icons.info;
14709    }
14710
14711    /**
14712     * Close stock modal
14713     */
14714    function closeStockModal() {
14715        const modal = document.getElementById('stock-modal-overlay');
14716        if (modal) {
14717            modal.classList.remove('show');
14718            setTimeout(() => {
14719                modal.remove();
14720            }, 200);
14721        }
14722    }
14723
14724    // Make closeStockModal globally available
14725    if (typeof window !== 'undefined') {
14726        window.closeStockModal = closeStockModal;
14727    }
14728
14729    /**
14730     * Get element data attribute with fallback
14731     */
14732    function getDataAttribute(element, attr) {
14733        if (element.dataset) {
14734            return element.dataset[attr];
14735        } else {
14736            return element.getAttribute('data-' + attr);
14737        }
14738    }
14739
14740    /**
14741     * Validate stock before cart action
14742     */
14743    async function validateBeforeCart(productId, variantId, qty, action = 'add') {
14744        try {
14745            // SMART VALIDATION: For simple products, check if we already have cached stock info
14746            // If buttons are enabled and cached info shows stock available, skip API validation
14747            if (window._currentStockInfo && window._currentStockInfo.can_add_to_cart) {
14748        
14749                return true;
14750            }
14751            
14752            // For variant products or when cached info is not available, do API validation
14753            const stockInfo = await checkStock(productId, variantId, qty);
14754            
14755            if (!stockInfo.ok) {
14756                showStockModal({ 
14757                    message: stockInfo.error || 'Gagal mengecek stok',
14758                    is_active: false 
14759                }, action);
14760                return false;
14761            }
14762
14763            if (!stockInfo.can_add_to_cart) {
14764                showStockModal(stockInfo, action);
14765                return false;
14766            }
14767
14768            return true; // Stock available, proceed
14769
14770        } catch (error) {
14771            console.error('Stock validation error:', error);
14772            showStockModal({ 
14773                message: 'Terjadi kesalahan saat mengecek stok. Silakan coba lagi.',
14774                is_active: false 
14775            }, action);
14776            return false;
14777        }
14778    }
14779
14780    /**
14781     * Handle button click with stock validation
14782     */
14783    function handleButtonClick(event, action) {
14784        const button = event.target;
14785        
14786        
14787        // FIRST CHECK: Is button marked as stock-blocked? (Anti-tampering)
14788        if (button.hasAttribute('data-stock-blocked')) {
14789            event.preventDefault();
14790            event.stopImmediatePropagation();
14791            
14792            const reason = button.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
14793            showStockModal(reason);
14794            
14795            // Show security warning
14796            if (window.showToast) {
14797                window.showToast({
14798                    type: 'warning',
14799                    title: 'Aksi Tidak Diizinkan',
14800                    message: 'Produk ini sedang habis stok. Silakan pilih varian lain.',
14801                    duration: 3000
14802                });
14803            }
14804            return false;
14805        }
14806        
14807        // SECOND CHECK: Real-time stock validation against cached stock info
14808        if (window._currentStockInfo && !window._currentStockInfo.can_add_to_cart) {
14809            event.preventDefault();
14810            event.stopImmediatePropagation();
14811            
14812            showStockModal(window._currentStockInfo.message || 'Stok tidak tersedia');
14813            
14814            // Re-enforce button disabling
14815            button.disabled = true;
14816            button.style.opacity = '0.5';
14817            button.style.cursor = 'not-allowed';
14818            button.setAttribute('data-stock-blocked', 'true');
14819            
14820            return false;
14821        }
14822        
14823        // Skip if already processing
14824        if (button.classList.contains(config.loadingClass)) {
14825            event.preventDefault();
14826            return false;
14827        }
14828
14829        // Get product data from button attributes or form
14830        let productId = parseInt(getDataAttribute(button, 'productId') || getDataAttribute(button, 'product-id') || 0);
14831        let variantId = parseInt(getDataAttribute(button, 'variantId') || getDataAttribute(button, 'variant-id') || 0);
14832        let qty = parseInt(getDataAttribute(button, 'qty') || 1);
14833
14834        // Try to get from global variables (set by product detail page)
14835        if (productId <= 0 && typeof window.PD_ID !== 'undefined') {
14836            productId = parseInt(window.PD_ID || 0);
14837        }
14838
14839        // Try to get from parent form if not in button
14840        if (productId <= 0) {
14841            const form = button.closest ? button.closest('form') : null;
14842            if (form) {
14843                const pidAttr = form.getAttribute('data-pid');
14844                const pidInput = form.querySelector('[name="product_id"]');
14845                const vidInput = form.querySelector('[name="variant_id"]');
14846                const qtyInput = form.querySelector('[name="qty"]');
14847                
14848                if (pidAttr) productId = parseInt(pidAttr);
14849                if (pidInput) productId = parseInt(pidInput.value || 0);
14850                if (vidInput) variantId = parseInt(vidInput.value || 0);
14851                if (qtyInput) qty = parseInt(qtyInput.value || 1);
14852            }
14853        }
14854
14855        // CRITICAL: Always get current variant from selector (overrides button attributes)
14856        // This ensures we validate against the currently selected variant, not cached button data
14857        const variantSelector = document.getElementById('pd-variant-id');
14858        if (variantSelector) {
14859            const currentVariantId = parseInt(variantSelector.value || 0);
14860            if (currentVariantId > 0) {
14861                variantId = currentVariantId;
14862            }
14863        }
14864
14865
14866        if (productId <= 0) {
14867            return true; // Let default handler deal with it
14868        }
14869
14870        // Prevent default and validate stock
14871        event.preventDefault();
14872        event.stopPropagation();
14873        
14874        // Show loading state
14875        button.classList.add(config.loadingClass);
14876        const originalDisabled = button.disabled;
14877        button.disabled = true;
14878        
14879        
14880        validateBeforeCart(productId, variantId, qty, action)
14881            .then(isValid => {
14882                if (isValid) {
14883                    // Stock OK, proceed with original action
14884                    if (action === 'cart' && typeof window.addToCart === 'function') {
14885                        window.addToCart(productId, qty, variantId);
14886                    } else if (action === 'buy' && typeof window.buyNow === 'function') {
14887                        // Call buyNow from pd-buynow.js without parameters (it gets data internally)
14888                        window.buyNow();
14889                    } else {
14890                        // Add fallback cart/buy logic here if needed
14891                    }
14892                }
14893            })
14894            .catch(error => {
14895            })
14896            .finally(() => {
14897                // Remove loading state
14898                button.classList.remove(config.loadingClass);
14899                button.disabled = originalDisabled;
14900            });
14901
14902        return false;
14903    }
14904
14905    /**
14906     * Handle mobile button click with stock validation
14907     */
14908    function handleMobileButtonClick(event, action) {
14909        const button = event.target;
14910        
14911        
14912        // FIRST CHECK: Is button marked as stock-blocked? (Anti-tampering)
14913        if (button.hasAttribute('data-stock-blocked')) {
14914            event.preventDefault();
14915            event.stopImmediatePropagation();
14916            
14917            const reason = button.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
14918            showStockModal(reason);
14919            
14920            // Show security warning
14921            if (window.showToast) {
14922                window.showToast({
14923                    type: 'warning',
14924                    title: 'Aksi Tidak Diizinkan',
14925                    message: 'Produk ini sedang habis stok. Silakan pilih varian lain.',
14926                    duration: 3000
14927                });
14928            }
14929            return false;
14930        }
14931        
14932        // SECOND CHECK: Real-time stock validation against cached stock info
14933        if (window._currentStockInfo && !window._currentStockInfo.can_add_to_cart) {
14934            event.preventDefault();
14935            event.stopImmediatePropagation();
14936            
14937            showStockModal(window._currentStockInfo.message || 'Stok tidak tersedia');
14938            
14939            // Re-enforce button disabling
14940            button.disabled = true;
14941            button.style.opacity = '0.5';
14942            button.style.cursor = 'not-allowed';
14943            button.setAttribute('data-stock-blocked', 'true');
14944            
14945            return false;
14946        }
14947        
14948        // Skip if already processing
14949        if (button.classList.contains(config.loadingClass)) {
14950            event.preventDefault();
14951            return false;
14952        }
14953
14954        // Get product data from mobile FAB container
14955        let productId = 0;
14956        let variantId = 0;
14957        let qty = 1;
14958
14959        // Try to get from mobile FAB data-pid
14960        const mobileFab = document.getElementById('pd-mobile-fab');
14961        if (mobileFab) {
14962            const pidAttr = getDataAttribute(mobileFab, 'pid');
14963            if (pidAttr) productId = parseInt(pidAttr);
14964        }
14965
14966        // Try to get from global variables (set by product detail page)
14967        if (productId <= 0 && typeof window.PD_ID !== 'undefined') {
14968            productId = parseInt(window.PD_ID || 0);
14969        }
14970
14971        // CRITICAL: Always get current variant from selector (real-time)
14972        // This ensures we validate against the currently selected variant
14973        const variantSelector = document.getElementById('pd-variant-id');
14974        if (variantSelector) {
14975            variantId = parseInt(variantSelector.value || 0);
14976        }
14977
14978        // Try to get quantity from quantity input
14979        const qtySelector = document.querySelector('[name="qty"]');
14980        if (qtySelector) {
14981            qty = parseInt(qtySelector.value || 1);
14982        }
14983        
14984        // Ensure minimum quantity is 1
14985        qty = Math.max(1, qty);
14986
14987
14988        if (productId <= 0) {
14989            return true; // Let default handler deal with it
14990        }
14991
14992        // Event already prevented at interceptor level, just proceed with validation
14993        
14994        // Show loading state
14995        button.classList.add(config.loadingClass);
14996        const originalDisabled = button.disabled;
14997        const originalText = button.textContent;
14998        button.disabled = true;
14999        button.textContent = action === 'cart' ? 'Validasi...' : 'Validasi...';
15000        
15001        
15002        // PREEMPTIVE BLOCKING: Set blocking flags BEFORE validation to prevent race conditions
15003        if (window.addToCart && typeof window.addToCart === 'function') {
15004            window.addToCart._stockValidatorBlocked = true;
15005        }
15006        
15007        // Also preemptively block other cart functions
15008        const originalAddToCartSubmit = window.addToCartSubmit;
15009        const originalPdAddToCartSubmit = window._pdAddToCartSubmit;
15010        
15011        // Temporarily block ALL cart functions during validation
15012        window.addToCartSubmit = (...args) => {
15013            return Promise.resolve(false);
15014        };
15015        
15016        if (originalPdAddToCartSubmit) {
15017            window._pdAddToCartSubmit = (...args) => {
15018                return Promise.resolve(false);
15019            };
15020        }
15021        
15022        validateBeforeCart(productId, variantId, qty, action)
15023            .then(isValid => {
15024                if (isValid) {
15025                    // Stock OK, proceed with original action
15026                    
15027                    if (action === 'cart') {
15028                        // For mobile cart, we need to recreate the original behavior
15029                        // Strategy: Try multiple methods to ensure cart add + toast success
15030                        
15031                        
15032                        let cartAddSuccess = false;
15033                        let errorMessage = '';
15034                        
15035                        // Method 1: Try original addToCart function
15036                        const originalAddToCart = window.addToCart && window.addToCart._original;
15037                        if (originalAddToCart && !cartAddSuccess) {
15038                            try {
15039                                const result = originalAddToCart(productId, qty, variantId);
15040                                
15041                                // Handle both sync and async returns
15042                                if (result && typeof result.then === 'function') {
15043                                    result.then(() => {
15044                                        cartAddSuccess = true;
15045                                    }).catch(err => {
15046                                        errorMessage = err.message || 'addToCart failed';
15047                                    });
15048                                } else {
15049                                    cartAddSuccess = true;
15050                                }
15051                            } catch (error) {
15052                                errorMessage = error.message || 'addToCart exception';
15053                            }
15054                        }
15055                        
15056                        // Method 2: Try pd-addtocart.js function (backup method)
15057                        if (typeof window._pdAddToCartSubmit === 'function' && !cartAddSuccess) {
15058                            try {
15059                                
15060                                // Create fake form for pd-addtocart.js
15061                                const fakeForm = document.createElement('form');
15062                                const qtyInput = document.createElement('input');
15063                                qtyInput.name = 'qty';
15064                                qtyInput.value = qty;
15065                                fakeForm.appendChild(qtyInput);
15066                                
15067                                // Set variant selector value for pd-addtocart to read
15068                                const variantSelector = document.getElementById('pd-variant-id');
15069                                if (variantSelector) {
15070                                    variantSelector.value = variantId;
15071                                }
15072                                
15073                                const fakeEvent = { 
15074                                    preventDefault: () => {},
15075                                    target: fakeForm
15076                                };
15077                                
15078                                // Call pd-addtocart.js function (this should handle toast internally)
15079                                const result = window._pdAddToCartSubmit(fakeEvent, productId);
15080                                if (result && typeof result.then === 'function') {
15081                                    result.then(() => {
15082                                        cartAddSuccess = true;
15083                                    });
15084                                } else {
15085                                    cartAddSuccess = true;
15086                                }
15087                            } catch (error) {
15088                            }
15089                        }
15090                        
15091                        // Method 3: Direct addToCart call (fallback)
15092                        if (typeof window.addToCart === 'function' && !cartAddSuccess) {
15093                            try {
15094                                const result = window.addToCart(productId, qty, variantId);
15095                                if (result && typeof result.then === 'function') {
15096                                    result.then(() => {
15097                                        cartAddSuccess = true;
15098                                    });
15099                                } else {
15100                                    cartAddSuccess = true;
15101                                }
15102                            } catch (error) {
15103                            }
15104                        }
15105                        
15106                        // Always show success toast for mobile (regardless of which method worked)
15107                        // This ensures user gets feedback for successful cart addition
15108                        setTimeout(() => {
15109                            if (window.showToast) {
15110                                
15111                                // Get product details for toast
15112                                const getVariantSummaryText = () => {
15113                                    const vs = document.getElementById('pd-variant-summary');
15114                                    if (!vs) return '';
15115                                    const items = Array.from(vs.querySelectorAll('.vs-item strong'))
15116                                        .map(el => el.textContent.trim())
15117                                        .filter(Boolean);
15118                                    return items.length ? items.join(' / ') : '';
15119                                };
15120                                
15121                                const escapeHtml = (s) => String(s||'').replace(/[&<>"']/g, m=>({
15122                                    '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'
15123                                }[m]));
15124                                
15125                                const variantText = getVariantSummaryText();
15126                                const skuText = (function(){ 
15127                                    const el = document.getElementById('pd-sku'); 
15128                                    return el ? el.textContent.trim() : (window.PD_SKU || 'N/A'); 
15129                                })();
15130                                const productName = window.PD_NAME || 'Product';
15131                                
15132                                window.showToast({
15133                                    type: 'success',
15134                                    title: 'Berhasil ditambahkan ke Keranjang',
15135                                    message: `<strong>${escapeHtml(productName)}</strong>${variantText ? ` · <em>${escapeHtml(variantText)}</em>` : ''}<br>SKU: ${escapeHtml(skuText)}
15135 · Qty: ${qty}`,
15136                                    duration: 5000,
15137                                    actions: [
15138                                        { 
15139                                            label: 'Lihat Keranjang', 
15140                                            onClick: () => { window.location.href = '/keranjang'; }
15141                                        },
15142                                        { 
15143                                            label: 'Lanjut Belanja', 
15144                                            variant: 'alt', 
15145                                            onClick: () => {} 
15146                                        }
15147                                    ]
15148                                });
15149                            } else {
15150                            }
15151                        }, 100); // Small delay to ensure cart operation completes first
15152                        
15153                    } else if (action === 'buy') {
15154                        // For mobile buy, call buyNow
15155                        if (typeof window.buyNow === 'function') {
15156                            window.buyNow();
15157                        }
15158                    }
15159                } else {
15160                    // Stock validation FAILED - absolutely NO cart function calls allowed
15161                    
15162                    // DO NOT CALL ANY CART FUNCTIONS - only show modal (already handled by validateBeforeCart)
15163                }
15164                
15165                // Always restore functions after validation (success or failure)
15166                setTimeout(() => {
15167                    // Remove blocking flag
15168                    if (window.addToCart && typeof window.addToCart === 'function') {
15169                        window.addToCart._stockValidatorBlocked = false;
15170                    }
15171                    
15172                    // Restore other functions
15173                    if (originalAddToCartSubmit && typeof originalAddToCartSubmit === 'function') {
15174                        window.addToCartSubmit = originalAddToCartSubmit;
15175                    }
15176                    if (originalPdAddToCartSubmit && typeof originalPdAddToCartSubmit === 'function') {
15177                        window._pdAddToCartSubmit = originalPdAddToCartSubmit;
15178                    }
15179                    
15180                }, 1000); // Shorter timeout since we want to restore quickly after success
15181            })
15182            .catch(error => {
15183                // On error, also block the action
15184            })
15185            .finally(() => {
15186                // Remove loading state
15187                button.classList.remove(config.loadingClass);
15188                button.disabled = originalDisabled;
15189                button.textContent = originalText;
15190            });
15191
15192        return false;
15193    }
15194
15195    /**
15196     * Intercept cart buttons
15197     */
15198    function interceptCartButtons() {
15199
15200        
15201        // CRITICAL: Remove existing mobile cart handlers from product-detail.php
15202        setTimeout(() => {
15203            const mobileCartBtn = document.getElementById('mf-cart');
15204            if (mobileCartBtn) {
15205                // Clone the button to remove ALL existing event listeners
15206                const newBtn = mobileCartBtn.cloneNode(true);
15207                mobileCartBtn.parentNode.replaceChild(newBtn, mobileCartBtn);
15208
15209                
15210                // Add our own handler to the new button
15211                newBtn.addEventListener('click', function(event) {
15212                    event.preventDefault();
15213                    event.stopPropagation();
15214                    event.stopImmediatePropagation();
15215                    handleMobileButtonClick(event, 'cart');
15216                    return false;
15217                }, true);
15218            }
15219        }, 50);
15220        
15221        // Note: Add to cart buttons are now handled via form submission override (addToCartSubmit)
15222        // This provides better integration with existing pd-addtocart.js toast system
15223
15224        // Buy now buttons - updated selectors (desktop + mobile)
15225        addEventListener(document, 'click', function(event) {
15226            const target = event.target;
15227            
15228            // Desktop buy buttons
15229            if (target.classList.contains('btn-buy') ||           // Product detail buy button
15230                target.classList.contains('btn-buy-now') || 
15231                target.classList.contains('buy-now-btn') || 
15232                target.id === 'btn-buy' ||                        // Specific ID match
15233                getDataAttribute(target, 'action') === 'buy-now' ||
15234                getDataAttribute(target, 'action') === 'buy') {
15235                
15236                handleButtonClick(event, 'buy');
15237            }
15238            
15239            // Mobile buy buttons
15240            else if (target.classList.contains('mf-buy') ||       // Mobile fab buy button
15241                     target.id === 'mf-buy') {                   // Mobile buy button ID
15242                
15243                handleMobileButtonClick(event, 'buy');
15244            }
15245            
15246            // Mobile cart buttons - ENHANCED BLOCKING
15247            else if (target.classList.contains('mf-cart') ||      // Mobile fab cart button
15248                     target.id === 'mf-cart') {                  // Mobile cart button ID
15249                
15250                
15251                // Immediately prevent all event propagation
15252                event.preventDefault();
15253                event.stopPropagation();
15254                event.stopImmediatePropagation();
15255                
15256                handleMobileButtonClick(event, 'cart');
15257                
15258                // Return false to ensure no other handlers run
15259                return false;
15260            }
15261        }, true);
15261 // Use capturing phase to ensure we intercept before other handlers
15262        
15263        // Additional protection: Override addToCart function to add mobile button state checking
15264        const originalAddToCart = typeof window.addToCart === 'function' ? window.addToCart : null;
15265        if (originalAddToCart) {
15266            window.addToCart = async function(...args) {
15267                // Check if this call is coming from a mobile button that's currently being validated
15268                const mobileCartBtn = document.getElementById('mf-cart');
15269                if (mobileCartBtn && mobileCartBtn.classList.contains('loading-stock')) {
15270                    return Promise.reject(new Error('Mobile validation in progress - cart action blocked'));
15271                }
15272                
15273                // Check if functions are temporarily blocked (during stock failure)
15274                if (window.addToCart._stockValidatorBlocked) {
15275                    return Promise.reject(new Error('Stock validation failed - cart action blocked'));
15276                }
15277                
15278                // Allow normal calls
15279                return originalAddToCart.apply(this, args);
15280            };
15281            
15282            // Store reference to original for restoration
15283            window.addToCart._original = originalAddToCart;
15284        }
15285    }
15286
15287    /**
15288     * Setup global click interceptor for security
15289     * This catches ALL clicks on cart/buy buttons and validates stock BEFORE any other handler
15290     */
15291    function setupGlobalClickInterceptor() {
15292        // Add capture-phase listener to catch clicks before other handlers
15293        document.addEventListener('click', function(event) {
15294            const target = event.target;
15295            
15296            // Check if this is a cart/buy button
15297            const isCartButton = target.matches('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart') ||
15298                                target.closest('.btn-add-cart, .mf-cart, #mf-cart, #btn-add-cart');
15299            const isBuyButton = target.matches('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy') ||
15300                               target.closest('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy');
15301                               
15302            if (!isCartButton && !isBuyButton) {
15303                return; // Not our target, allow normal processing
15304            }
15305            
15306            const button = isCartButton ? 
15307                (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')) :
15308                (target.matches('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy') ? target : target.closest('.btn-buy-now, .mf-buy, #mf-buy, #btn-buy'));
15309            
15310            // IMMEDIATE BLOCK: Prevent any processing first
15311            event.preventDefault();
15312            event.stopImmediatePropagation();
15313            
15314            // SECURITY CHECK 1: Is button marked as stock-blocked?
15315            if (button && button.hasAttribute('data-stock-blocked')) {
15316                
15317                const reason = button.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
15318                showStockModal(reason);
15319                
15320                // Show security alert
15321                if (window.showToast) {
15322                    window.showToast({
15323                        type: 'error',
15324                        title: 'Aksi Diblokir',
15325                        message: 'Produk ini sedang habis stok dan tidak dapat ditambahkan ke keranjang.',
15326                        duration: 4000
15327                    });
15328                }
15329                
15330                return false;
15331            }
15332            
15333            // SECURITY CHECK 2: Real-time validation against cached stock info
15334            if (window._currentStockInfo && !window._currentStockInfo.can_add_to_cart) {
15335                
15336                showStockModal(window._currentStockInfo.message || 'Stok tidak tersedia');
15337                
15338                // Re-enforce button disabling
15339                if (button) {
15340                    button.disabled = true;
15341                    button.style.opacity = '0.5';
15342                    button.style.cursor = 'not-allowed';
15343                    button.setAttribute('data-stock-blocked', 'true');
15344                    button.setAttribute('data-stock-reason', window._currentStockInfo.message || 'Stok tidak tersedia');
15345                }
15346                
15347                return false;
15348            }
15349            
15350            // SECURITY CHECK 3: Smart client-side validation
15351            const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15352            const variantSelector = document.getElementById('pd-variant-id');
15353            const variantId = variantSelector ? parseInt(variantSelector.value || 0
15353) : 0;
15354            
15355            if (productId > 0) {
15356                // SMART VALIDATION: If we already have cached stock info showing stock is available,
15357                // and buttons are enabled, proceed immediately without re-validation
15358                if (window._currentStockInfo && window._currentStockInfo.can_add_to_cart && 
15359                    button && !button.disabled && !button.hasAttribute('data-stock-blocked')) {
15360                    
15361                    // Allow the action to proceed immediately with toast
15362                    if (isCartButton) {
15363                        // Trigger cart action with proper toast handling
15364                        if (typeof window._pdAddToCartSubmit === 'function') {
15365                            // Use pd-addtocart.js function for proper toast handling
15366                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15367                            if (form) {
15368                                const fakeEvent = { 
15369                                    target: form,
15370                                    preventDefault: () => {}
15371                                };
15372                                window._pdAddToCartSubmit(fakeEvent, productId);
15373                            }
15374                        } else if (typeof window.addToCartSubmit === 'function') {
15375                            // Fallback to addToCartSubmit
15376                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15377                            if (form) {
15378                                const fakeEvent = { 
15379                                    target: form,
15380                                    preventDefault: () => {}
15381                                };
15382                                window.addToCartSubmit(fakeEvent, productId);
15383                            }
15384                        } else if (typeof window.addToCart === 'function') {
15385                            // Direct addToCart call
15386                            window.addToCart(productId, 1, variantId);
15387                        }
15388                    } else if (isBuyButton) {
15389                        // Trigger buy action
15390                        if (typeof window.buyNow === 'function') {
15391                            window.buyNow();
15392                        }
15393                    }
15394                    
15395                    return false; // Action completed
15396                }
15397                
15398                // Get stock info from embedded variant data (for variant products)
15399                const stockInfo = getStockFromEmbeddedData(variantId);
15400                
15401                if (stockInfo && stockInfo.hasStock) {
15402                    
15403                    // Update global stock cache
15404                    window._currentStockInfo = {
15405                        can_add_to_cart: true,
15406                        is_in_stock: true,
15407                        available_stock: stockInfo.stock,
15408                        message: 'Stok tersedia'
15409                    };
15410                    
15411                    // IMPORTANT: Check if button was explicitly blocked by variant logic (e.g., pre-order exhausted)
15412                    // If data-stock-blocked is set, respect it and don't enable the button
15413                    const isExplicitlyBlocked = button.getAttribute('data-stock-blocked') === 'true';
15414                    
15415                    if (!isExplicitlyBlocked) {
15416                        // Remove stock blocking if it was set and enable button
15417                        button.removeAttribute('data-stock-blocked');
15418                        button.removeAttribute('data-stock-reason');
15419                        button.disabled = false;
15420                        button.style.opacity = '';
15421                        button.style.cursor = '';
15422                        button.classList.remove(config.disabledClass);
15423                    } else {
15424                        // Button is blocked by variant logic, show reason
15425                        const reason = button.getAttribute('data-stock-reason') || 'Tidak tersedia';
15426                        console.log('Button blocked by variant logic:', reason);
15427                        e.preventDefault();
15428                        e.stopPropagation();
15429                        return false;
15430                    }
15431                    
15432                    // Allow the action to proceed immediately with toast
15433                    if (isCartButton) {
15434                        // Trigger cart action with proper toast handling
15435                        if (typeof window._pdAddToCartSubmit === 'function') {
15436                            // Use pd-addtocart.js function for proper toast handling
15437                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15438                            if (form) {
15439                                const fakeEvent = { 
15440                                    target: form,
15441                                    preventDefault: () => {}
15442                                };
15443                                window._pdAddToCartSubmit(fakeEvent, productId);
15444                            }
15445                        } else if (typeof window.addToCartSubmit === 'function') {
15446                            // Fallback to addToCartSubmit
15447                            const form = document.getElementById('pd-buy-form') || button.closest('form');
15448                            if (form) {
15449                                const fakeEvent = { 
15450                                    target: form,
15451                                    preventDefault: () => {}
15452                                };
15453                                window.addToCartSubmit(fakeEvent, productId);
15454                            }
15455                        } else if (typeof window.addToCart === 'function') {
15456                            // Direct addToCart call
15457                            window.addToCart(productId, 1, variantId);
15458                        }
15459                    } else if (isBuyButton) {
15460                        // Trigger buy action
15461                        if (typeof window.buyNow === 'function') {
15462                            window.buyNow();
15463                        }
15464                    }
15465                } else if (stockInfo && !stockInfo.hasStock) {
15466                    
15467                    // Update global stock cache
15468                    window._currentStockInfo = {
15469                        can_add_to_cart: false,
15470                        is_in_stock: false,
15471                        available_stock: stockInfo.stock,
15472                        message: `Stok tidak tersedia${stockInfo.sku ? ` untuk ${stockInfo.sku}` : ''}`
15473                    };
15474                    
15475                    // Force block button
15476                    button.disabled = true;
15477                    button.style.opacity = '0.5';
15478                    button.style.cursor = 'not-allowed';
15479                    button.setAttribute('data-stock-blocked', 'true');
15480                    button.setAttribute('data-stock-reason', window._currentStockInfo.message);
15481                    
15482                    // Show modal with variant info
15483                    const modalMessage = stockInfo ? 
15484                        `Varian yang dipilih (${stockInfo.sku}) sedang habis stok. Silakan pilih varian lain.` :
15485                        'Produk ini sedang habis stok. Silakan pilih varian lain.';
15486                    
15487                    showStockModal(modalMessage);
15488                    
15489                    // Show security warning
15490                    if (window.showToast) {
15491                        window.showToast({
15492                            type: 'warning',
15493                            title: 'Stok Tidak Tersedia',
15494                            message: 'Varian yang dipilih sedang habis stok. Silakan pilih varian lain.',
15495                            duration: 3000
15496                        });
15497                    }
15498                } else {
15499                    // stockInfo is null (simple product) - this means embedded data is not available
15500                    // For simple products, if buttons are enabled, we should proceed (trust the API response from updateStockDisplay)
15501                    if (button && !button.disabled && !button.hasAttribute('data-stock-blocked')) {
15502                        
15503                        // Allow the action to proceed immediately with toast
15504                        if (isCartButton) {
15505                            // Trigger cart action with proper toast handling
15506                            if (typeof window._pdAddToCartSubmit === 'function') {
15507                                // Use pd-addtocart.js function for proper toast handling
15508                                const form = document.getElementById('pd-buy-form') || button.closest('form');
15509                                if (form) {
15510                                    const fakeEvent = { 
15511                                        target: form,
15512                                        preventDefault: () => {}
15513                                    };
15514                                    window._pdAddToCartSubmit(fakeEvent, productId);
15515                                }
15516                            } else if (typeof window.addToCartSubmit === 'function') {
15517                                // Fallback to addToCartSubmit
15518                                const form = document.getElementById('pd-buy-form') || button.closest('form');
15519                                if (form) {
15520                                    const fakeEvent = { 
15521                                        target: form,
15522                                        preventDefault: () => {}
15523                                    };
15524                                    window.addToCartSubmit(fakeEvent, productId);
15525                                }
15526                            } else if (typeof window.addToCart === 'function') {
15527                                // Direct addToCart call
15528                                window.addToCart(productId, 1, variantId);
15529                            }
15530                        } else if (isBuyButton) {
15531                            // Trigger buy action
15532                            if (typeof window.buyNow === 'function') {
15533                                window.buyNow();
15534                            }
15535                        }
15536                    } else {
15537                        // Button is disabled, don't allow action
15538                        showStockModal('Produk ini sedang tidak tersedia.');
15539                    }
15540                }
15541            } else {
15542                // No product ID, block action
15543                showStockModal('Data produk tidak valid.');
15544            }
15545            
15546            return false; // Always block at capture phase
15547            
15548        }, true); // Use capture phase to run BEFORE other listeners
15549        
15550
15551    }
15552
15553    /**
15554     * Initialize button data attributes from current page
15555     */
15556    function initializeButtonAttributes() {
15557        // Get product data from page globals or form
15558        let productId = 0, variantId = 0;
15559        
15560        // Try to get from global variables (set by PHP)
15561        if (window.PD_ID) productId = parseInt(window.PD_ID);
15562        
15563        // Try to get from form
15564        const form = document.getElementById('pd-buy-form');
15565        if (form) {
15566            const pidAttr = form.getAttribute('data-pid');
15567            if (pidAttr) productId = parseInt(pidAttr);
15568            
15569            const varInput = document.getElementById('pd-variant-id');
15570            if (varInput) variantId = parseInt(varInput.value || 0);
15571        }
15572        
15573        // Try to get from mobile FAB
15574        const mobileFab = document.getElementById('pd-mobile-fab');
15575        if (mobileFab && productId <= 0) {
15576            const pidAttr = getDataAttribute(mobileFab, 'pid');
15577            if (pidAttr) productId = parseInt(pidAttr);
15578        }
15579        
15580        // Set attributes on desktop buttons
15581        const desktopButtons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, #btn-add-cart, #btn-buy, #btn-buy-voucher');
15582        desktopButtons.forEach(button => {
15583            if (productId > 0) {
15584                button.setAttribute('data-product-id', productId);
15585                if (variantId > 0) {
15586                    button.setAttribute('data-variant-id', variantId);
15587                }
15588            }
15589        });
15590        
15591        // Set attributes on mobile buttons
15592        const mobileButtons = document.querySelectorAll('.mf-cart, .mf-buy, #mf-cart, #mf-buy');
15593        mobileButtons.forEach(button => {
15594            if (productId > 0) {
15595                button.setAttribute('data-product-id', productId);
15596                if (variantId > 0) {
15597                    button.setAttribute('data-variant-id', variantId);
15598                }
15599            }
15600        });
15601        
15602
15603        
15604        // Initial stock display update on page load
15605        if (productId > 0) {
15606            setTimeout(() => {
15607                updateStockDisplay(productId, variantId);
15608
15609            }, 100);
15610        }
15611    }
15612
15613    /**
15614     * Update button attributes when variant changes
15615     */
15616    function updateButtonAttributesForVariant(variantId) {
15617        const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15618        
15619        if (productId <= 0) return;
15620        
15621        // Update desktop buttons
15622        const desktopButtons = document.querySelectorAll('.btn-add-cart, .btn-buy-now, #btn-add-cart, #btn-buy, #btn-buy-voucher');
15623        desktopButtons.forEach(button => {
15624            button.setAttribute('data-product-id', productId);
15625            if (variantId > 0) {
15626                button.setAttribute('data-variant-id', variantId);
15627            } else {
15628                button.removeAttribute('data-variant-id');
15629            }
15630        });
15631        
15632        // Update mobile buttons
15633        const mobileButtons = document.querySelectorAll('.mf-cart, .mf-buy, #mf-cart, #mf-buy');
15634        mobileButtons.forEach(button => {
15635            button.setAttribute('data-product-id', productId);
15636            if (variantId > 0) {
15637                button.setAttribute('data-variant-id', variantId);
15638            } else {
15639                button.removeAttribute('data-variant-id');
15640            }
15641        });
15642        
15643
15644    }
15645
15646    /**
15647     * Setup variant change integration with existing product-detail.php handlers
15648     */
15649    function setupVariantChangeIntegration() {
15650        // Strategy: Hook into existing applyVariant function instead of adding new listener
15651        // This prevents conflicts with existing variant change functionality (gallery, pricing, etc.)
15652        
15653        // Method 1: Hook into existing applyVariant function
15654        setTimeout(() => {
15655            if (typeof window.applyVariant === 'function') {
15656                const originalApplyVariant = window.applyVariant;
15657                window.applyVariant = function(v, focusImage) {
15658                    // Call original function first
15659                    const result = originalApplyVariant.call(this, v, focusImage);
15660                    
15661                // Then add our stock validator logic
15662                const variantId = v ? (v.id || v.ID || 0) : 0;
15663                
15664                // Update button attributes for stock validator
15665                updateButtonAttributesForVariant(variantId);
15666                
15667                // Update stock display using embedded data (instant)
15668                const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15669                if (productId > 0) {
15670                    updateStockDisplay(productId, variantId);
15671                }                    return result;
15672                };
15673                
15674
15675            } else {
15676
15677                setupVariantObserver();
15678            }
15679        }, 500);
15680        
15681        // Method 2: Direct observation of hidden input value changes (fallback)
15682        setTimeout(() => {
15683            setupVariantObserver();
15684        }, 1000);
15685    }
15686
15687    /**
15688     * Setup variant observer as fallback when applyVariant hook fail
15688s
15689     */
15690    function setupVariantObserver() {
15691        const variantInput = document.getElementById('pd-variant-id');
15692        if (variantInput) {
15693            let lastVariantId = parseInt(variantInput.value || '0');
15694            
15695            // Use MutationObserver to watch for value changes
15696            if (window.MutationObserver) {
15697                const observer = new MutationObserver((mutations) => {
15698                    mutations.forEach((mutation) => {
15699                        if (mutation.type === 'attributes' && mutation.attributeName === 'value') {
15700                            const newVariantId = parseInt(variantInput.value || '0');
15701                            if (newVariantId !== lastVariantId) {
15702                    
15703                                updateButtonAttributesForVariant(newVariantId);
15704                                
15705                                // Update stock display with embedded data
15706                                const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15707                                if (productId > 0) {
15708                                    updateStockDisplay(productId, newVariantId);
15709                                }
15710                                
15711                                lastVariantId = newVariantId;
15712                            }
15713                        }
15714                    });
15715                });
15716                
15717                observer.observe(variantInput, { 
15718                    attributes: true, 
15719                    attributeFilter: ['value'] 
15720                });
15721                
15722
15723            }
15724            
15725            // Additional polling as ultimate fallback
15726            setInterval(() => {
15727                const currentVariantId = parseInt(variantInput.value || '0');
15728                if (currentVariantId !== lastVariantId) {
15729        
15730                    updateButtonAttributesForVariant(currentVariantId);
15731                    
15732                    // Update stock display with embedded data
15733                    const productId = window.PD_ID ? parseInt(window.PD_ID) : 0;
15734                    if (productId > 0) {
15735                        updateStockDisplay(productId, currentVariantId);
15736                    }
15737                    
15738                    lastVariantId = currentVariantId;
15739                }
15740            }, 1000);
15741            
15742
15743        }
15744    }
15745
15746    /**
15747     * Update stock display and enforce stock-based button blocking using embedded data
15748     */
15749    function updateStockDisplay(productId, variantId = 0) {
15750
15751        
15752        // Get stock info from embedded data (instant)
15753        const embeddedStockInfo = getStockFromEmbeddedData(variantId);
15754        
15755        let stockInfo;
15756        if (embeddedStockInfo) {
15757            // Convert embedded data to standard format
15758            stockInfo = {
15759                can_add_to_cart: embeddedStockInfo.hasStock,
15760                is_in_stock: embeddedStockInfo.hasStock,
15761                available_stock: embeddedStockInfo.stock,
15762                message: embeddedStockInfo.hasStock ? 'Stok tersedia' : `Stok habis${embeddedStockInfo.sku ? ` untuk ${embeddedStockInfo.sku}` : ''}`,
15763                product_name: window.PD_NAME || 'Produk',
15764                sku: embeddedStockInfo.sku
15765            };
15766            
15767
15768        } else {
15769            // Fallback to API call if embedded data not available
15770
15771            checkStock(productId, variantId, 1)
15772                .then(apiStockInfo => {
15773                    updateStockDisplayWithInfo(apiStockInfo, productId, variantId);
15774                })
15775                .catch(error => {
15776                    console.error('Failed to update stock display via API:', error);
15777                });
15778            return;
15779        }
15780        
15781        updateStockDisplayWithInfo(stockInfo, productId, variantId);
15782    }
15783
15784    /**
15785     * Update stock display with provided stock info
15786     */
15787    function updateStockDisplayWithInfo(stockInfo, productId, variantId) {
15788        // Store current stock status globally for blocking checks
15789        window._currentStockInfo = stockInfo;
15790        
15791        // Update stock indicators
15792        const stockIndicators = document.querySelectorAll('.stock-indicator');
15793        stockIndicators.forEach(indicator => {
15794            if (stockInfo.is_in_stock) {
15795                indicator.classList.add('in-stock');
15796                indicator.classList.remove('out-of-stock');
15797            } else {
15798                indicator.classList.add('out-of-stock');
15799                indicator.classList.remove('in-stock');
15800            }
15801        });
15802        
15803        // Update stock count
15804        const stockCounts = document.querySelectorAll('.stock-count');
15805        stockCounts.forEach(count => {
15806            count.textContent = stockInfo.available_stock || 0;
15807        });
15808        
15809        // CRITICAL: For digital products, DO NOT modify button states here
15810        // PHP already handles button disable/enable based on LICENSE_POOL count
15811        // Only physical products need JavaScript stock validation
15812        const isDigitalProduct = window.IS_DIGITAL === true || window.IS_DIGITAL === 'true';
15813        
15814        if (isDigitalProduct) {
15815            // For digital products: ONLY update global stock info, don't touch buttons
15816            // Let PHP-rendered disabled state remain authoritative
15817            console.log('[Stock Validator] Digital product detected - skipping button state modification');
15818            return; // Exit early, preserve PHP-set button states
15819        }
15820        
15821        // ENHANCED: Force disable buttons for out-of-stock with anti-tampering (PHYSICAL PRODUCTS ONLY)
15822        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');
15823        allButtons.forEach(button => {
15824            if (stockInfo.can_add_to_cart) {
15825                // STOCK AVAILABLE - ENABLE BUTTONS
15826                button.classList.remove(config.disabledClass);
15827                button.disabled = false;
15828                button.style.opacity = '';
15829                button.style.cursor = '';
15830                
15831                // CRITICAL: Remove stock blocking attributes when stock becomes available
15832                button.removeAttribute('data-stock-blocked');
15833                button.removeAttribute('data-stock-reason');
15834                
15835
15836            } else {
15837                // OUT OF STOCK - FORCE DISABLE WITH ANTI-TAMPERING
15838                button.classList.add(config.disabledClass);
15839                button.disabled = true;
15840                button.style.opacity = '0.5';
15841                button.style.cursor = 'not-allowed';
15842                
15843                // Mark as stock-blocked to prevent re-enabling
15844                button.setAttribute('data-stock-blocked', 'true');
15845                button.setAttribute('data-stock-reason', stockInfo.message || 'Stok tidak tersedia');
15846                
15847
15848            }
15849        });
15850        
15851        // Start aggressive button monitoring for out-of-stock variants
15852        if (!stockInfo.can_add_to_cart) {
15853            enforceStockDisabling(productId, variantId);
15854        }
15855        
15856
15857    }
15858
15859    /**
15860     * Aggressively enforce button disabling for out-of-stock items
15861     * CRITICAL: For digital products, DO NOT modify button states (PHP is authoritative)
15862     */
15863    function enforceStockDisabling(productId, variantId) {
15864        // CRITICAL: Skip enforcement for digital products - PHP handles button states
15865        const isDigitalProduct = window.IS_DIGITAL === true || window.IS_DIGITAL === 'true';
15866        if (isDigitalProduct) {
15867            console.log('[Stock Validator] Digital product - skipping button enforcement');
15868            return; // Exit early, let PHP-rendered disabled state remain
15869        }
15870        
15871        const monitorInterval = setInterval(() => {
15872            // Check if variant changed
15873            const currentVariantSelector = document.getElementById('pd-variant-id');
15874            const currentVariantId = currentVariantSelector ? parseInt(currentVariantSelector.value || 0) : 0;
15875            
15876            if (currentVariantId !== variantId) {
15877                // Variant changed, stop monitoring this variant
15878                clearInterval(monitorInterval);
15879
15880                return;
15881            }
15882            
15883            // Check current stock status using embedded data (real-time check)
15884            const currentStockInfo = getStockFromEmbeddedData(currentVariantId);
15885            
15886            if (currentStockInfo && !currentStockInfo.hasStock) {
15887                // Still out of stock - continue monitoring for tampering
15888                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');
15889                buttons.forEach(button => {
15890                    // Check if button was manually enabled (inspect attack)
15891                    if (!button.disabled || button.style.opacity !== '0.5') {
15892                        
15893                        // Force re-disable
15894                        button.disabled = true;
15895                        button.style.opacity = '0.5';
15896                        button.style.cursor = 'not-allowed';
15897                        button.setAttribute('data-stock-blocked', 'true');
15898                        button.setAttribute('data-stock-reason', `Stok tidak tersedia${currentStockInfo.sku ? ` untuk ${currentStockInfo.sku}` : ''}`);
15899                        
15900                        // Show warning toast
15901                        if (window.showToast) {
15902                            window.showToast({
15903                                type: 'warning',
15904                                title: 'Aksi Tidak Diizinkan',
15905                                message: 'Produk ini sedang habis stok. Silakan pilih varian lain.',
15906                                duration: 3000
15907                            });
15908                        }
15909                    }
15910                });
15911            } else {
15912                // Stock available OR embedded data shows stock - stop monitoring
15913                clearInterval(monitorInterval);
15914
15915                // For PHYSICAL products only, enable buttons
15916                // For DIGITAL products, skip (PHP is authoritative)
15917                const isDigitalProduct = window.IS_DIGITAL === true || window.IS_DIGITAL === 'true';
15918                
15919                if (!isDigitalProduct && currentStockInfo && currentStockInfo.hasStock) {
15920                    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');
15921                    buttons.forEach(button => {
15922                        button.disabled = false;
15923                        button.style.opacity = '';
15924                        button.style.cursor = '';
15925                        button.removeAttribute('data-stock-blocked');
15926                        button.removeAttribute('data-stock-reason');
15927                        button.classList.remove(config.disabledClass);
15928                    });
15929
15930                }
15931            }
15932        }, 500); // Check every 500ms for tampering attempts
15933        
15934        // Auto-stop monitoring after 30 seconds to prevent memory leaks
15935        setTimeout(() => {
15936            clearInterval(monitorInterval);
15937
15938        }, 30000);
15939    }
15940
15941    /**
15942     * Override addToCartSubmit function to add stock validation
15943     * This function now works with pd-addtocart.js integration
15944     */
15945    function overrideAddToCartSubmitFunction() {
15946        // Check if pd-addtocart.js is already loaded
15947        const pdAddToCartExists = typeof window.addToCartSubmit === 'function' && 
15948                                  window.addToCartSubmit.toString().includes('showToast');
15949        
15950        if (pdAddToCartExists) {
15951
15952            
15953            // Store the pd-addtocart.js function
15954            const pdAddToCartSubmit = window.addToCartSubmit;
15955            window._pdAddToCartSubmit = pdAddToCartSubmit;
15956            
15957            // Override with stock validation that calls pd-addtocart.js
15958            window.addToCartSubmit = async function(e, productId) {
15959                
15960                // Don't prevent default here - let pd-addtocart handle it if stock is valid
15961                
15962                try {
15963                    // Extract product data from form (same way as pd-addtocart.js)
15964                    const form = e.target;
15965                    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
15966                    
15967                    // CRITICAL: Always get current variant from selector (real-time)
15968                    let variantId = 0;
15969                    const variantSelector = document.getElementById('pd-variant-id');
15970                    if (variantSelector) {
15971                        variantId = parseInt(variantSelector.value || '0', 10) || 0;
15972                    }
15973                    
15974                    if (!productId || productId <= 0) {
15975                        return pdAddToCartSubmit(e, productId);
15976                    }
15977
15978
15979                    // Validate stock before proceeding
15980                    const isValid = await validateBeforeCart(productId, variantId, qty, 'cart');
15981                    
15982                    if (isValid) {
15983                        // Stock OK, call pd-addtocart.js function (it will handle toast)
15984                        return pdAddToCartSubmit(e, productId);
15985                    } else {
15986                        // Prevent the form submission
15987                        e.preventDefault();
15988                        // Stock validation failed, modal already shown by validateBeforeCart
15989                        return false;
15990                    }
15991                    
15992                } catch (error) {
15993                    e.preventDefault();
15994                    // On error, show modal and don't proceed
15995                    showStockModal({ 
15996                        message: 'Terjadi kesalahan saat memvalidasi stok. Silakan coba lagi.',
15997                        is_active: false 
15998                    }, 'cart');
15999                    return false;
16000                }
16001            };
16002        } else {
16003            // Original logic for when pd-addtocart.js is not present
16004            const originalAddToCartSubmit = typeof window.addToCartSubmit === 'function' ? window.addToCartSubmit : null;
16005            
16006            if (originalAddToCartSubmit) {
16007                window._originalAddToCartSubmit = originalAddToCartSubmit;
16008            }
16009            
16010            // Override with stock validation
16011            window.addToCartSubmit = async function(e, productId) {
16012                
16013                e.preventDefault();
16014                
16015                try {
16016                    const form = e.target;
16017                    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16018                    
16019                    // CRITICAL: Always get current variant from selector (real-time)
16020                    let variantId = 0;
16021                    const variantSelector = document.getElementById('pd-variant-id');
16022                    if (variantSelector) {
16023                        variantId = parseInt(variantSelector.value || '0', 10) || 0;
16024                    }
16025                    
16026                    if (!productId || productId <= 0) {
16027                        if (originalAddToCartSubmit) return originalAddToCartSubmit(e, productId);
16028                        return false;
16029                    }
16030
16031
16032                    const isValid = await validateBeforeCart(productId, variantId, qty, 'cart');
16033                    
16034                    if (isValid) {
16035                        if (originalAddToCartSubmit) {
16036                            return originalAddToCartSubmit(e, productId);
16037                        } else {
16038                            if (typeof window.addToCart === 'function') {
16039                                await window.addToCart(productId, qty, variantId);
16040                            }
16041                        }
16042                    } else {
16043                    }
16044                    
16045                } catch (error) {
16046                    showStockModal({ 
16047                        message: 'Terjadi kesalahan saat memvalidasi stok. Silakan coba lagi.',
16048                        is_active: false 
16049                    }, 'cart');
16050                }
16051                
16052                return false;
16053            };
16054        }
16055
16056
16057    }
16058
16059    /**
16060     * Override buyNow function to add stock validation
16061     */
16062    function overrideBuyNowFunction() {
16063        // Store original buyNow function if it exists
16064        const originalBuyNow = typeof window.buyNow === 'function' ? window.buyNow : null;
16065        
16066        // Override with stock validation
16067        window.buyNow = async function(e) {
16068            
16069            try {
16070                // Get product data (same way as pd-buynow.js)
16071                const form = document.getElementById('pd-buy-form');
16072                if (!form) {
16073                    if (originalBuyNow) return originalBuyNow(e);
16074                    return;
16075                }
16076
16077                const productId = parseInt(form.dataset.pid || '0', 10);
16078                const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16079                
16080                // CRITICAL: Always get current variant from selector (real-time)
16081                let variantId = 0;
16082                const variantSelector = document.getElementById('pd-variant-id');
16083                if (variantSelector) {
16084                    variantId = parseInt(variantSelector.value || '0', 10) || 0;
16085                }
16086                
16087                if (!productId || productId <= 0) {
16088                    if (originalBuyNow) return originalBuyNow(e);
16089                    return;
16090                }
16091
16092
16093                // Validate stock before proceeding
16094                const isValid = await validateBeforeCart(productId, variantId, qty, 'buy');
16095                
16096                if (isValid) {
16097                    // Stock OK, call original buyNow function
16098                    if (originalBuyNow) {
16099                        return originalBuyNow(e);
16100                    } else {
16101                    }
16102                } else {
16103                    // Stock validation failed, modal already shown by validateBeforeCart
16104                }
16105                
16106            } catch (error) {
16107                // On error, show modal and don't proceed
16108                showStockModal({ 
16109                    message: 'Terjadi kesalahan saat memvalidasi stok. Silakan coba lagi.',
16110                    is_active: false 
16111                }, 'buy');
16112            }
16113        };
16114
16115
16116    }
16117
16118    /**
16119     * Initialize stock validation
16120     */
16121    function init() {
16122        // Wait for DOM ready
16123        if (document.readyState === 'loading') {
16124            addEventListener(document, 'DOMContentLoaded', function() {
16125        initializeButtonAttributes();
16126        setupVariantChangeIntegration(); // Setup variant change integration
16127        interceptCartButtons();
16128        setupGlobalClickInterceptor(); // Setup global click security interceptor                // Override functions after other scripts load
16129                // Use multiple timeouts to catch late-loading scripts like pd-addtocart.js
16130                setTimeout(() => {
16131                    overrideAddToCartSubmitFunction();
16132                    overrideBuyNowFunction();
16133                }, 100);
16134                
16135                setTimeout(() => {
16136                    // Re-check and re-override if pd-addtocart.js loaded later
16137                    overrideAddToCartSubmitFunction();
16138                }, 500);
16139                
16140                setTimeout(() => {
16141                    // Final check for very late loading scripts
16142                    overrideAddToCartSubmitFunction();
16143                }, 1000);
16144                
16145
16146            });
16147        } else {
16148            initializeButtonAttributes();
16149            setupVariantChangeIntegration(); // Setup variant change integration
16150            interceptCartButtons();
16151            setupGlobalClickInterceptor(); // Setup global click security interceptor
16152            
16153            // Override functions after other scripts load
16154            // Use multiple timeouts to catch late-loading scripts like pd-addtocart.js
16155            setTimeout(() => {
16156                overrideAddToCartSubmitFunction();
16157                overrideBuyNowFunction();
16158            }, 100);
16159            
16160            setTimeout(() => {
16161                // Re-check and re-override if pd-addtocart.js loaded later
16162                overrideAddToCartSubmitFunction();
16163            }, 500);
16164            
16165            setTimeout(() => {
16166                // Final check for very late loading scripts
16167                overrideAddToCartSubmitFunction();
16168            }, 1000);
16169            
16170
16171        }
16172    }
16173
16174    // Public API
16175    return {
16176        init: init,
16177        checkStock: checkStock,
16178        validateBeforeCart: validateBeforeCart,
16179        updateStockDisplay: updateStockDisplay,
16180        showStockModal: showStockModal,
16181        config: config
16182    };
16183})();
16184
16185// Auto-initialize
16186if (typeof window !== 'undefined') {
16187    StockValidator.init();
16188}
16189
16190</script>
vendor: 2 bytes, line 16190
16190
16191<script data-inlined="pd-addtocart.js">
16192(function(){
16193  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
16194  function getVariantSummaryText(){
16195    const vs = document.getElementById('pd-variant-summary');
16196    if (!vs) return '';
16197    const items = Array.from(vs.querySelectorAll('.vs-item strong')).map(el => el.textContent.trim()).filter(Boolean);
16198    return items.length ? items.join(' / ') : '';
16199  }
16200  window.addToCartSubmit = async function(e, productId){
16201    e.preventDefault();
16202    try {
16203      const form = e.target;
16204      const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16205      const variantId = parseInt(document.getElementById('pd-variant-id')?.value || '0', 10) || 0;
16206      await (window.addToCart ? window.addToCart(productId, qty, variantId) : Promise.reject(new Error('addToCart tidak tersedia')));
16207
16208      if (window.PlazaAnalytics) {
16209        window.PlazaAnalytics.track('add_to_cart', { slug: String(window._pit_slug || '') });
16210      }
16211
16212      const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
16213      const sku  = (function(){ const el=document.getElementById('pd-sku'); return el?el.textContent.trim(): (window.PD_SKU||''); })();
16214      const variantText = getVariantSummaryText();
16215
16216      if (window.showToast) {
16217        window.showToast({
16218          type: 'success',
16219          title: 'Berhasil ditambahkan ke Keranjang',
16220          message: `<strong>${esc(name)}</strong>${variantText?` · <em>${esc(variantText)}</em>`:''}<br>SKU: ${esc(sku)} · Qty: ${qty}`,
16221          duration: 5000,
16222          actions: [
16223            { label: 'Lihat Keranjang', onClick: ()=> { window.location.href = '/keranjang'; } },
16224            { label: 'Lanjut Belanja', variant: 'alt', onClick: ()=>{} }
16225          ]
16226        });
16227      }
16228    } catch (err) {
16229      if (window.showToast) {
16230        window.showToast({ type:'warn', title:'Gagal menambahkan', message: String(err?.message||'Terjadi kesalahan'), duration: 4500 });
16231      } else {
16232        console.error(err);
16233      }
16234    }
16235    return false;
16236  };
16237})();
16238</script>
vendor: 2 bytes, line 16238
16238
16239<script data-inlined="pd-buynow.js">
16240(function(){
16241  
16242  // CSRF Token Helper
16243  function getCsrfToken() {
16244    return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
16245  }
16246  
16247  // Utility functions
16248  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
16249  
16250  function getVariantSummaryText(){
16251    const vs = document.getElementById('pd-variant-summary');
16252    if (!vs) return '';
16253    const items = Array.from(vs.querySelectorAll('.vs-item strong')).map(el => el.textContent.trim()).filter(Boolean);
16254    return items.length ? items.join(' / ') : '';
16255  }
16256
16257  function getCurrentProductData(){
16258    const form = document.getElementById('pd-buy-form');
16259    if (!form) return null;
16260
16261    const productId = parseInt(form.dataset.pid || '0', 10);
16262    const qty = parseInt(form.querySelector('[name=qty]')?.value || '1', 10) || 1;
16263    const variantId = parseInt(document.getElementById('pd-variant-id')?.value || '0', 10) || 0;
16264    
16265    if (!productId || productId <= 0) {
16266      throw new Error('Product ID tidak valid');
16267    }
16268
16269    if (qty <= 0) {
16270      throw new Error('Quantity harus lebih dari 0');
16271    }
16272
16273    return { productId, qty, variantId };
16274  }
16275
16276  function createBuyNowPayload(productId, qty, variantId) {
16277    // Get product metadata
16278    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
16279    
16280    // Get correct price, SKU, and weight from selected variant data instead of DOM elements
16281    let price = 0;
16282    let sku = '';
16283    let weight = 0;
16284    let dimensions = null;
16285    
16286    if (variantId > 0 && typeof window.variants !== 'undefined' && Array.isArray(window.variants)) {
16287      // Find the selected variant in the variants array
16288      const selectedVariant = window.variants.find(v => v.id === variantId);
16289      if (selectedVariant) {
16290        price = selectedVariant.final || 0;
16291        sku = selectedVariant.sku || '';
16292        weight = selectedVariant.weight || 0;
16293        
16294        // Include dimensions if available
16295        if (selectedVariant.length_cm || selectedVariant.width_cm || selectedVariant.height_cm) {
16296          dimensions = {
16297            length_cm: selectedVariant.length_cm || 0,
16298            width_cm: selectedVariant.width_cm || 0,
16299            height_cm: selectedVariant.height_cm || 0
16300          };
16301        }
16302        
16303        // Debug info to verify fix is working
16304        if (window.console && window.console.debug) {
16305          console.debug('BuyNow: Using variant data', {
16306            variantId: variantId,
16307            price: price,
16308            sku: sku,
16309            weight: weight,
16310            dimensions: dimensions,
16311            variantData: selectedVariant
16312          });
16313        }
16314      }
16315    }
16316    
16317    // Fallback to DOM elements if variant data not found
16318    if (price === 0) {
16319      const priceTxt = document.getElementById('pd-price-current')?.textContent || '';
16320      price = parseInt(String(priceTxt).replace(/[^\d]/g,''), 10) || 0;
16321      
16322      if (window.console && window.console.debug) {
16323        console.debug('BuyNow: Using DOM price fallback', { 
16324          priceTxt: priceTxt, 
16325          price: price,
16326          variantId: variantId,
16327          reason: variantId > 0 ? 'variant_data_not_found' : 'no_variant_selected'
16328        });
16329      }
16330    }
16331    
16332    if (!sku) {
16333      const skuEl = document.getElementById('pd-sku');
16334      sku = skuEl ? skuEl.textContent.trim() : (window.PD_SKU || '');
16335    }
16336    
16337    const imgEl = document.getElementById('pd-main-img');
16338    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
16339
16340    const variantText = getVariantSummaryText();
16341    
16342    // Get pre-order data if product is pre-order
16343    const preorderData = window.PD_PREORDER_DATA || null;
16344    
16345    return {
16346      product_id: productId,
16347      qty: qty,
16348      variant_id: variantId,
16349      meta: {
16350        name,
16351        sku,
16352        price,
16353        image_url,
16354        variant_text: variantText,
16355        weight: weight,
16356        dimensions: dimensions
16357      },
16358      is_preorder: preorderData ? 'Y' : 'N',
16359      preorder_estimated_arrival: preorderData ? preorderData.estimated_arrival : null,
16360      preorder_note: preorderData ? preorderData.note : ''
16361    };
16362  }
16363
16364  /**
16365   * Main Buy Now function
16366   * Performs validation and redirects to checkout with buy_now mode
16367   */
16368  window.buyNow = async function(e) {
16369    if (e) e.preventDefault();
16370    
16371    try {
16372      // Check user login status
16373      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
16374                        document.body.classList.contains('logged-in') ||
16375                        window.userLoggedIn === true ||
16376                        document.body.dataset.userLoggedIn === 'true';
16377      
16378      if (!isLoggedIn) {
16379        // Redirect to login with return URL to current page
16380        const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
16381        window.location.href = '/login?return=' + currentUrl;
16382        return;
16383      }
16384
16385      // Get current product data
16386      const { productId, qty, variantId } = getCurrentProductData();
16387      
16388      // Create payload for buy now
16389      const payload = createBuyNowPayload(productId, qty, variantId);
16390      
16391      // Store buy now data in sessionStorage for checkout page
16392      sessionStorage.setItem('buyNowData', JSON.stringify(payload));
16393
16394      // Track add_to_cart (buy now counts as purchase intent)
16395      if (window.PlazaAnalytics) {
16396        window.PlazaAnalytics.track('add_to_cart', { slug: String(window._pit_slug || '') });
16397      }
16398
16399      // Redirect to checkout with buy_now mode
16400      window.location.href = '/checkout?mode=buy_now';
16401      
16402    } catch (err) {
16403      console.error('Buy Now error:', err);
16404      
16405      if (window.showToast) {
16406        window.showToast({ 
16407          type: 'warn', 
16408          title: 'Gagal memproses', 
16409          message: String(err?.message || 'Terjadi kesalahan saat memproses pembelian'), 
16410          duration: 4500 
16411        });
16412      } else {
16413        alert(err.message || 'Terjadi kesalahan saat memproses pembelian');
16414      }
16415    }
16416  };
16417
16418  /**
16419   * Alternative Buy Now with confirmation dialog
16420   */
16421  window.buyNowWithConfirm = async function(e) {
16422    if (e) e.preventDefault();
16423    
16424    try {
16425      const { productId, qty, variantId } = getCurrentProductData();
16426      
16427      const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : (document.getElementById('pd-title')?.textContent || '');
16428      const variantText = getVariantSummaryText();
16429      const priceTxt = document.getElementById('pd-price-current')?.textContent || 'Rp0';
16430      
16431      const confirmMessage = `Lanjut ke checkout?\n\nProduk: ${name}${variantText ? `\nVariant: ${variantText}` : ''}\nQty: ${qty}\nHarga: ${priceTxt}`;
16432      
16433      if (!confirm(confirmMessage)) {
16434        return;
16435      }
16436      
16437      // Proceed with buy now
16438      await window.buyNow(e);
16439      
16440    } catch (err) {
16441      console.error('Buy Now with confirm error:', err);
16442      alert(err.message || 'Terjadi kesalahan');
16443    }
16444  };
16445
16446  /**
16447   * Attach event listener for Buy with Voucher button
16448   */
16449  function setupBuyButtons() {
16450    // Setup desktop buy button (regular buy now without voucher)
16451    const buyBtn = document.getElementById('btn-buy');
16452    if (buyBtn) {
16453      buyBtn.addEventListener('click', function(e) {
16454        e.preventDefault();
16455        if (buyBtn.disabled) return;
16456        if (window.buyNow) {
16457          window.buyNow(e);
16458        }
16459      });
16460    }
16461    
16462    // Setup desktop buy with voucher button
16463    const voucherBtn = document.getElementById('btn-buy-voucher');
16464    if (voucherBtn) {
16465      voucherBtn.addEventListener('click', function(e) {
16466        e.preventDefault();
16467        
16468        // Validate stock availability before proceeding
16469        if (this.disabled || this.getAttribute('data-stock-blocked') === 'true') {
16470          const stockReason = this.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
16471          
16472          if (window.showToast) {
16473            window.showToast({
16474              type: 'warn',
16475              title: 'Stok Tidak Tersedia',
16476              message: stockReason,
16477              duration: 3500
16478            });
16479          } else {
16480            alert(stockReason);
16481          }
16482          return;
16483        }
16484        
16485        const voucherCode = this.dataset.voucherCode;
16486        if (voucherCode) {
16487          window.buyNowWithVoucher(e, voucherCode);
16488        }
16489      });
16490    }
16491    
16492  }
16493  
16494  // CRITICAL: Attach mobile button handler at DOCUMENT level with CAPTURE phase
16495  // This is the ONLY way to guarantee it fires regardless of other scripts
16496  document.addEventListener('click', function(e) {
16497    // Check if click is on mobile buy button or its children
16498    const target = e.target.closest('#mf-buy');
16499    if (!target) return; // Not our button
16500    
16501    e.preventDefault();
16502    e.stopPropagation();
16503    e.stopImmediatePropagation();
16504    
16505    if (target.disabled) return;
16506    
16507    // Get voucher code from parent FAB's data attribute
16508    const mobileFab = document.getElementById('pd-mobile-fab');
16509    const voucherCode = mobileFab ? (mobileFab.getAttribute('data-voucher-code') || '') : '';
16510    
16511    if (voucherCode && voucherCode.trim() !== '' && window.buyNowWithVoucher) {
16512      window.buyNowWithVoucher(e, voucherCode);
16513    } else if (window.buyNow) {
16514      window.buyNow(e);
16515    }
16516  }, true); // CAPTURE PHASE - fires BEFORE any bubble phase handlers
16517  
16518  // Execute immediately if DOM already loaded, otherwise wait
16519  if (document.readyState === 'loading') {
16520    document.addEventListener('DOMContentLoaded', setupBuyButtons);
16521  } else {
16522    setupBuyButtons();
16523  }
16524
16525  /**
16526   * Buy Now with Auto-Applied Voucher
16527   * Applies voucher to session before redirecting to checkout (like cart does)
16528   * 
16529   * @param {Event} e - Click event
16530   * @param {string} voucherCode - Voucher code to apply
16531   */
16532  window.buyNowWithVoucher = async function(e, voucherCode) {
16533    if (e) e.preventDefault();
16534    
16535    console.log('[buyNowWithVoucher] Called with voucher code:', voucherCode);
16536    
16537    try {
16538      // CRITICAL: Check stock availability first
16539      const voucherBtn = document.getElementById('btn-buy-voucher');
16540      if (voucherBtn) {
16541        if (voucherBtn.disabled || voucherBtn.getAttribute('data-stock-blocked') === 'true') {
16542          const stockReason = voucherBtn.getAttribute('data-stock-reason') || 'Stok tidak tersedia';
16543          
16544          if (window.showToast) {
16545            window.showToast({
16546              type: 'warn',
16547              title: 'Stok Tidak Tersedia',
16548              message: stockReason,
16549              duration: 3500
16550            });
16551          } else {
16552            alert(stockReason);
16553          }
16554          return;
16555        }
16556      }
16557      
16558      // Check login status first
16559      const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
16560                        document.body.classList.contains('logged-in') ||
16561                        window.userLoggedIn === true ||
16562                        document.body.dataset.userLoggedIn === 'true';
16563      
16564      console.log('[buyNowWithVoucher] User logged in:', isLoggedIn);
16565      
16566      if (!isLoggedIn) {
16567        const currentUrl = encodeURIComponent(window.location.pathname + window.location.search);
16568        window.location.href = '/login?return=' + currentUrl;
16569        return;
16570      }
16571
16572      // Get current product data
16573      const { productId, qty, variantId } = getCurrentProductData();
16574      const payload = createBuyNowPayload(productId, qty, variantId);
16575      
16576      console.log('[buyNowWithVoucher] Product data:', { productId, qty, variantId });
16577      
16578      // Get category path for voucher validation
16579      const categoryPath = window.PD_CATEGORY_PATH || '';
16580      
16581      // Step 1: Clear any existing voucher first (important for clean state)
16582      console.log('[buyNowWithVoucher] Step 1: Clearing existing voucher...');
16583      try {
16584        await fetch('/api/voucher/remove.php', {
16585          method: 'POST',
16586          headers: { 
16587            'X-CSRF-Token': getCsrfToken()
16588          },
16589          body: JSON.stringify({
16590            csrf_token: getCsrfToken()
16591          }),
16592          credentials: 'same-origin'
16593        });
16594      } catch (e) {
16595        // Ignore error if no voucher exists
16596        console.debug('No existing voucher to remove or removal failed');
16597      }
16598      
16599      // Step 2: Apply voucher to session (like cart does)
16600      console.log('[buyNowWithVoucher] Step 2: Applying voucher to session...');
16601      const applyRes = await fetch('/api/voucher/apply.php', {
16602        method: 'POST',
16603        headers: { 
16604          'Content-Type': 'application/json',
16605          'X-CSRF-Token': getCsrfToken()
16606        },
16607        credentials: 'same-origin',
16608        body: JSON.stringify({
16609          csrf_token: getCsrfToken(),
16610          code: voucherCode,
16611          items: [{
16612            pid: productId,
16613            price: payload.meta.price,
16614            qty: qty,
16615            category_path: categoryPath
16616          }],
16617          shipping_cost: null // Will be calculated in checkout
16618        })
16619      });
16620      
16621      const applyData = await applyRes.json();
16622      
16623      console.log('[buyNowWithVoucher] Voucher apply response:', applyData);
16624      
16625      if (!applyData.success) {
16626        // Voucher invalid - show error and fallback to regular buy now
16627        console.warn('[buyNowWithVoucher] Voucher application failed:', applyData.message);
16628        if (window.showToast) {
16629          window.showToast({
16630            type: 'error',
16631            title: 'Voucher Tidak Valid',
16632            message: applyData.message || 'Voucher tidak dapat diterapkan untuk produk ini',
16633            duration: 4000
16634          });
16635        } else {
16636          alert(applyData.message || 'Voucher tidak dapat diterapkan');
16637        }
16638        
16639        // Fallback to regular buy now without voucher
16640        console.log('Voucher failed, proceeding with regular buy now');
16641        return window.buyNow(e);
16642      }
16643      
16644      // Step 3: Store buy now data in sessionStorage
16645      console.log('[buyNowWithVoucher] Step 3: Storing buy now data in sessionStorage');
16646      sessionStorage.setItem('buyNowData', JSON.stringify(payload));
16647      
16648      // Step 4: Redirect to checkout
16649      // Voucher sudah di session, checkout akan auto-load via loadAppliedVoucher()
16650      console.log('[buyNowWithVoucher] Step 4: Redirecting to checkout with voucher applied');
16651      window.location.href = '/checkout?mode=buy_now';
16652      
16653    } catch (err) {
16654      console.error('Buy Now with Voucher error:', err);
16655      
16656      if (window.showToast) {
16657        window.showToast({
16658          type: 'error',
16659          title: 'Terjadi Kesalahan',
16660          message: 'Gagal memproses pembelian dengan voucher. Silakan coba lagi.',
16661          duration: 3000
16662        });
16663      } else {
16664        alert('Gagal memproses pembelian. Silakan coba lagi.');
16665      }
16666    }
16667  };
16668})();
16669</script>
vendor: 2 bytes, line 16669
16669
16670<script data-inlined="pd-voucher-recalc.js">
16671/**
16672 * Product Detail - Voucher Discount Recalculation
16673 * 
16674 * Handles dynamic voucher discount recalculation when variant price changes
16675 * Supports both PERCENTAGE and FIXED_AMOUNT voucher types
16676 */
16677
16678(function() {
16679  'use strict';
16680
16681  /**
16682   * Calculate voucher discount based on type and price
16683   * 
16684   * @param {string} type - Voucher type: 'PERCENTAGE' or 'FIXED_AMOUNT'
16685   * @param {number} value - Voucher value (percentage or fixed amount)
16686   * @param {number} price - Current variant price
16687   * @param {number} maxDiscount - Maximum discount for percentage vouchers (0 = no limit)
16688   * @return {number} Calculated discount amount
16689   */
16690  function calculateVoucherDiscount(type, value, price, maxDiscount) {
16691    if (type === 'PERCENTAGE') {
16692      // Calculate percentage discount
16693      let discount = (price * value) / 100;
16694      
16695      // Apply max discount cap if set
16696      if (maxDiscount > 0 && discount > maxDiscount) {
16697        discount = maxDiscount;
16698      }
16699      
16700      return Math.round(discount);
16701    } else {
16702      // FIXED_AMOUNT - discount doesn't change with price
16703      return Math.round(value);
16704    }
16705  }
16706
16707  /**
16708   * Format number as Rupiah without 'Rp' prefix
16709   * 
16710   * @param {number} num - Number to format
16711   * @return {string} Formatted number (e.g., "20.000")
16712   */
16713  function formatNumber(num) {
16714    return Math.round(num).toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
16715  }
16716
16717  /**
16718   * Update voucher button discount label
16719   * 
16720   * @param {number} newPrice - New variant price
16721   */
16722  function updateVoucherDiscount(newPrice) {
16723    const voucherBtn = document.getElementById('btn-buy-voucher');
16724    if (!voucherBtn) return;
16725
16726    // Get voucher data from button attributes
16727    const voucherType = voucherBtn.dataset.voucherType;
16728    const voucherValue = parseFloat(voucherBtn.dataset.voucherValue) || 0;
16729    const voucherMax = parseFloat(voucherBtn.dataset.voucherMax) || 0;
16730
16731    if (!voucherType || voucherValue === 0) return;
16732
16733    // Calculate new discount
16734    const newDiscount = calculateVoucherDiscount(voucherType, voucherValue, newPrice, voucherMax);
16735
16736    // Update the discount label
16737    const saveLabel = voucherBtn.querySelector('.bv-save');
16738    if (saveLabel) {
16739      saveLabel.textContent = '-Rp' + formatNumber(newDiscount);
16740    }
16741
16742    // Also update mobile FAB voucher display if exists
16743    updateMobileFabVoucher(newDiscount);
16744  }
16745
16746  /**
16747   * Update mobile FAB voucher value
16748   * 
16749   * @param {number} discountAmount - New discount amount
16750   */
16751  function updateMobileFabVoucher(discountAmount) {
16752    const fab = document.getElementById('pd-mobile-fab');
16753    if (fab) {
16754      fab.dataset.voucherValue = discountAmount.toString();
16755    }
16756  }
16757
16758  /**
16759   * Listen to variant change events and recalculate discount
16760   */
16761  function initVoucherRecalculation() {
16762    window.addEventListener('pd-variant-changed', function(e) {
16763      const detail = e.detail || {};
16764      const newPrice = detail.price || 0;
16765
16766      if (newPrice > 0) {
16767        updateVoucherDiscount(newPrice);
16768      }
16769    });
16770  }
16771
16772  // Initialize when DOM is ready
16773  if (document.readyState === 'loading') {
16774    document.addEventListener('DOMContentLoaded', initVoucherRecalculation);
16775  } else {
16776    initVoucherRecalculation();
16777  }
16778
16779})();
16780
16781</script>
vendor: 2 bytes, line 16781
16781
16782<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>
16782
16783
16784<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
16785(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'}})}}
16786
16787function _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()}})();
16788
16789(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()}})})();
16790</script>
16790<footer class="site-footer footer-compact variant-stripes" role="contentinfo">
16791  <div class="ft-top ft-collapsible" id="ftTop">
16792    <div class="container ft-grid">
16793
16794      <div class="ft-col ft-help">
16795        <h4 class="ft-title">Butuh Bantuan?</h4>
16796        <p class="ft-desc">Hubungi kami melalui Official WhatsApp interaktif sekarang.</p>
16797        <a class="ft-wa-btn" href="https://pastikale.me/wamak" target="_blank" rel="nofollow noopener">
16798          <span class="ico" aria-hidden="true">
16799            <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>
16800          </span>
16801          Kirim Pesan
16802        </a>
16803
16804        <p class="ft-desc sm-gap">Ikuti kami:</p>
16805        <div class="ft-social">
16806          <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>
16807          <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
16807V7.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>
16808          <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>
16809        </div>
16810      </div>
16811
16812            <!-- Collapsible Content Wrapper (Non-Homepage Only) -->
16813      <div class="ft-collapsible-content" id="ftCollapsible">
16814      
16815      <div class="ft-col">
16816        <h4 class="ft-title">Tentang EDUTOTO</h4>
16817        <ul class="ft-links">
16818          <li><a href="https://norita.mx/">Tentang EDUTOTO</a></li>
16819          <li><a href="https://norita.mx/">Kontak EDUTOTO</a></li>
16820          <li><a href="https://norita.mx/">Lokasi Toko</a></li>
16821          <li><a href="https://norita.mx/">FAQ</a></li>
16822        </ul>
16823        <div class="ft-address">
16824          <p class="company">PT. Edutama Relaksasi Nusantara</p>
16825                      <p>Jl. Prof. Dr. Satrio Kav. 3-5, Kel. Karet Semanggi,</p>
16826                      <p>Kec. Setiabudi, Kota Jakarta Selatan, DKI Jakarta, 12930</p>
16827                  </div>
16828      </div>
16829
16830      <div class="ft-col">
16831        <h4 class="ft-title">Informasi</h4>
16832        <ul class="ft-links">
16833          <li><a href="/syarat-dan-ketentuan">Syarat &amp; Ketentuan</a></li>
16834          <li><a href="/kebijakan-privasi">Kebijakan Privasi</a></li>
16835          <li><a href="/kebijakan-cookie">Kebijakan Cookie</a></li>
16836          <li><a href="/pengiriman-dan-pengembalian">Pengiriman &amp; Pengembalian</a></li>
16837          <li><a href="/pembayaran-dan-cicilan">Pembayaran &amp; Cicilan</a></li>
16838          <li><a href="/garansi-dan-layanan">Garansi &amp; Layanan</a></li>
16839        </ul>
16840
16841        <div class="ft-payments payments-small" aria-label="Metode Pembayaran">
16842          <div class="pay-row">
16843            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-mandiri.png" alt="Mandiri" loading="lazy">
16844            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-bni.gif" alt="BNI" loading="lazy">
16845            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-bri.gif" alt="BRI" loading="lazy">
16846            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-qris.gif" alt="QRIS" loading="lazy">
16847            <img src="https://imgmahasuhu.io/images/2026/05/20/wopslot-bca.gif" alt="BCA" loading="lazy">
16848          </div>
16849        </div>
16850      </div>
16851
16852            </div>
16853      <!-- End Collapsible Content -->
16854      
16855      <!-- Expand Button (Mobile Only) -->
16856      <button class="ft-expand-btn" id="ftExpandBtn" aria-label="Lihat semua informasi footer">
16857        <span class="expand-text">Selengkapnya</span>
16858        <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
16859          <path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/>
16860        </svg>
16861      </button>
16862      
16863    </div>
16864  </div>
16865  <div class="ft-bottom">
16866    <div class="container ft-bottom-inner">
16867      <div class="ft-copy">
16868        <span>ANGIN-RIBUT &copy; <span id="yearNow">2026</span></span>
16869        <span>PT ANGIN-RIBUT. All rights reserved.</span>
16870      </div>
16871    </div>
16872  </div>
16873  
16874          <style nonce="se3iyzEHMc9FQEB4wSyd0w==">
16875            /* Chat Widget Styles - Lightweight & Minimal */
16876            .chat-widget {
16877                position: fixed;
16878                bottom: 20px;
16879                right: 20px;
16880                z-index: 9999;
16881                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16882            }
16883            
16884            /* Hide floating button on mobile - use bottom nav instead */
16885            @media (max-width: 640px) {
16886                .chat-toggle-btn {
16887                    display: none !important;
16888                }
16889                
16890                /* Ensure popup is still visible on mobile even when button is hidden */
16891                .chat-widget {
16892                    display: block !important;
16893                }
16894                
16895                .chat-popup {
16896                    display: none; /* Hidden by default, shown via JS */
16897                }
16898                
16899                .chat-popup[style*="display: block"],
16900                .chat-popup[style*="display:block"] {
16901                    display: block !important;
16902                }
16903            }
16904            
16905            .chat-toggle-btn {
16906                width: 60px;
16907                height: 60px;
16908                border-radius: 50%;
16909                background: linear-gradient(135deg, #C9A227 0%, #8B6914 100%);
16910                border: none;
16911                color: white;
16912                cursor: pointer;
16913                box-shadow: 0 4px 12px rgba(201, 162, 39, 0.3);
16914                position: relative;
16915                transition: transform 0.2s, box-shadow 0.2s;
16916            }
16917            
16918            .chat-toggle-btn:hover {
16919                transform: scale(1.05);
16920                box-shadow: 0 6px 20px rgba(201, 162, 39, 0.4);
16921            }
16922            
16923            #chat-toggle-btn .chat-icon {
16924                position: absolute;
16925                top: 50%;
16926                left: 50%;
16927                transform: translate(-50%, -50%);
16928                width: 28px !important;
16929                height: 28px !important;
16930            }
16931            
16932            .chat-unread-badge {
16933                position: absolute;
16934                top: -5px;
16935                right: -5px;
16936                background: #ef4444;
16937                color: white;
16938                border-radius: 12px;
16939                padding: 2px 6px;
16940                font-size: 11px;
16941                font-weight: 600;
16942                min-width: 20px;
16943                text-align: center;
16944            }
16945            
16946            .chat-popup {
16947                position: absolute;
16948                bottom: 80px;
16949                right: 0;
16950                width: 380px;
16951                max-width: calc(100vw - 40px);
16952                height: 500px;
16953                max-height: calc(100vh - 120px);
16954                background: white;
16955                border-radius: 12px;
16956                box-shadow: 0 10px 40px rgba(0,0,0,0.2);
16957                display: flex;
16958                flex-direction: column;
16959                overflow: hidden;
16960            }
16961            
16962            .chat-popup-header {
16963                background: linear-gradient(135deg, #C9A227 0%, #8B6914 100%);
16964                color: white;
16965                padding: 16px 20px;
16966                display: flex;
16967                justify-content: space-between;
16968                align-items: center;
16969                flex-shrink: 0;
16970            }
16971            
16972            .chat-header-title {
16973                display: flex;
16974                flex-direction: column;
16975                gap: 2px;
16976                min-height: 0;
16977            }
16978            
16979            .chat-popup-header h3 {
16980                margin: 0;
16981                font-size: 18px;
16982                font-weight: 600;
16983                line-height: 1.2;
16984            }
16985            
16986            .chat-operating-hours {
16987                font-size: 11px;
16988                opacity: 0.9;
16989                font-weight: 400;
16990                display: flex;
16991                align-items: center;
16992                gap: 4px;
16993                line-height: 1.2;
16994            }
16995            
16996            .chat-operating-hours svg {
16997                width: 12px;
16998                height: 12px;
16999                opacity: 0.8;
17000                flex-shrink: 0;
17001            }
17002            
17003            .chat-header-actions {
17004                display: flex;
17005                align-items: center;
17006                gap: 8px;
17007            }
17008            
17009            /* Notification Toggle Button */
17010            .btn-notif-toggle {
17011                background: none;
17012                border: none;
17013                color: white;
17014                font-size: 18px;
17015                cursor: pointer;
17016                padding: 4px 8px;
17017                transition: all 0.2s;
17018                opacity: 0.8;
17019            }
17020            
17021            .btn-notif-toggle:hover {
17022                opacity: 1;
17023                transform: scale(1.1);
17024            }
17025            
17026            /* Icon states */
17027            .btn-notif-toggle[data-status="not-subscribed"] svg {
17028                opacity: 0.7;
17029            }
17030            
17031            .btn-notif-toggle[data-status="not-subscribed"]:hover svg {
17032                opacity: 1;
17033            }
17034            
17035            .btn-notif-toggle[data-status="subscribed"] svg {
17036                opacity: 1;
17037                animation: bellRing 2s ease-in-out;
17038            }
17039            
17040            .btn-notif-toggle[data-status="blocked"] svg {
17041                color: #ff6b6b;
17042            }
17043            
17044            .btn-notif-toggle[data-status="not-supported"] {
17045                display: none !important;
17046            }
17047            
17048            @keyframes bellRing {
17049                0%, 100% { transform: rotate(0deg); }
17050                10%, 30% { transform: rotate(-10deg); }
17051                20%, 40% { transform: rotate(10deg); }
17052            }
17053            
17054            /* Notification Info Bar */
17055            .chat-notif-info-bar {
17056                position: absolute;
17057                top: 64px;
17058                left: 0;
17059                right: 0;
17060                background: linear-gradient(to right, #e3f2fd, #f5f5f5);
17061                border-bottom: 1px solid #bbdefb;
17062                padding: 8px 12px;
17063                display: flex;
17064                align-items: center;
17065                gap: 8px;
17066                font-size: 12px;
17067                color: #616161;
17068                z-index: 10;
17069                animation: slideDown 0.3s ease;
17070            }
17071            
17072            .chat-notif-info-bar svg {
17073                flex-shrink: 0;
17074            }
17075            
17076            .chat-notif-info-bar .small {
17077                flex: 1;
17078                line-height: 1.4;
17079            }
17080            
17081            .chat-notif-info-bar .btn-dismiss {
17082                background: none;
17083                border: none;
17084                color: #9e9e9e;
17085                cursor: pointer;
17086                padding: 0 4px;
17087                font-size: 16px;
17088                transition: color 0.2s;
17089            }
17090            
17091            .chat-notif-info-bar .btn-dismiss:hover {
17092                color: #616161;
17093            }
17094            
17095            @keyframes slideDown {
17096                from {
17097                    max-height: 0;
17098                    opacity: 0;
17099                }
17100                to {
17101                    max-height: 50px;
17102                    opacity: 1;
17103                }
17104            }
17105            
17106            .chat-close-btn {
17107                background: none;
17108                border: none;
17109                color: white;
17110                font-size: 28px;
17111                line-height: 1;
17112                cursor: pointer;
17113                padding: 0;
17114                width: 30px;
17115                height: 30px;
17116            }
17117            
17118            .chat-popup-body {
17119                flex: 1 1 auto;
17120                display: flex;
17121                flex-direction: column;
17122                min-height: 0;
17123                overflow: hidden;
17124                height: calc(100% - 64px);
17125                position: relative;
17126                transition: padding-top 0.3s ease;
17127            }
17128            
17129            /* Add padding when info bar is visible */
17130            .chat-popup-body.has-info-bar {
17131                padding-top: 40px;
17132            }
17133            
17134            .chat-view {
17135                flex: 1 1 auto;
17136                display: none;
17137                flex-direction: column;
17138                min-height: 0;
17139                overflow: hidden;
17140            }
17141            
17142            #chat-list-view {
17143                display: flex;
17144            }
17145            
17146            #chat-messages-view {
17147                display: none;
17148            }
17149            
17150            #chat-messages-view.active {
17151                display: flex;
17152                flex-direction: column;
17153            }
17154            
17155            #chat-new-view {
17156                display: none;
17157            }
17158            
17159            #chat-new-view.active {
17160                display: flex;
17161            }
17162            
17163            .chat-conversations-list {
17164                flex: 1;
17165                overflow-y: auto;
17166                padding: 0;
17167                background: white;
17168            }
17169            
17170            /* Custom Scrollbar for Conversations List */
17171            .chat-conversations-list::-webkit-scrollbar {
17172                width: 6px;
17173            }
17174            
17175            .chat-conversations-list::-webkit-scrollbar-track {
17176                background: #f1f1f1;
17177            }
17178            
17179            .chat-conversations-list::-webkit-scrollbar-thumb {
17180                background: #C9A227;
17181                border-radius: 3px;
17182            }
17183            
17184            .chat-conversations-list::-webkit-scrollbar-thumb:hover {
17185                background: #8B6914;
17186            }
17187            
17188            /* Firefox */
17189            .chat-conversations-list {
17190                scrollbar-width: thin;
17191                scrollbar-color: #C9A227 #f1f1f1;
17192            }
17193            
17194            .chat-conversation-item {
17195                padding: 12px 16px;
17196                cursor: pointer;
17197                display: flex;
17198                gap: 12px;
17199                align-items: flex-start;
17200                transition: background 0.15s ease;
17201                color: #33371f;
17202                border-bottom: 1px solid #f0f0f0;
17203            }
17204            
17205            .chat-conversation-item:hover {
17206                background: #f5f5f5;
17207            }
17208            
17209            .chat-conversation-item.active {
17210                background: #FFF5EB;
17211            }
17212            
17213            /* WhatsApp-like Avatar */
17214            .conversation-avatar {
17215                width: 48px;
17216                height: 48px;
17217                border-radius: 50%;
17218                background: linear-gradient(135deg, #C9A227 0%, #8B6914 100%);
17219                flex-shrink: 0;
17220                display: flex;
17221                align-items: center;
17222                justify-content: center;
17223                color: white;
17224                font-weight: 600;
17225                font-size: 18px;
17226            }
17227            
17228            .conversation-avatar svg {
17229                width: 24px;
17230                height: 24px;
17231            }
17232            
17233            /* WhatsApp-like Content */
17234            .conversation-content {
17235                flex: 1;
17236                min-width: 0;
17237                display: flex;
17238                flex-direction: column;
17239                gap: 4px;
17240            }
17241            
17242            .conversation-header {
17243                display: flex;
17244                justify-content: space-between;
17245                align-items: center;
17246                gap: 8px;
17247            }
17248            
17249            .conversation-name {
17250                font-weight: 600;
17251                font-size: 15px;
17252                color: #111;
17253                white-space: nowrap;
17254                overflow: hidden;
17255                text-overflow: ellipsis;
17256            }
17257            
17258            .conversation-time {
17259                font-size: 12px;
17260                color: #7d8166;
17261                flex-shrink: 0;
17262            }
17263            
17264            .conversation-preview {
17265                display: flex;
17266                justify-content: space-between;
17267                align-items: center;
17268                gap: 8px;
17269            }
17270            
17271            .conversation-message {
17272                font-size: 14px;
17273                color: #667781;
17274                white-space: nowrap;
17275                overflow: hidden;
17276                text-overflow: ellipsis;
17277                margin: 0;
17278                flex: 1;
17279            }
17280            
17281            .conversation-unread {
17282                background: #25D366;
17283                color: white;
17284                border-radius: 12px;
17285                padding: 2px 8px;
17286                font-size: 12px;
17287                font-weight: 600;
17288                min-width: 20px;
17289                text-align: center;
17290                flex-shrink: 0;
17291            }
17292            
17293            /* Active item styling */
17294            .chat-conversation-item.active .conversation-name {
17295                color: #C9A227;
17296            }
17297            
17298            .chat-conversation-item.active .conversation-time {
17299                color: #C9A227;
17300            }
17301            
17302            .chat-new-btn {
17303                margin: 12px;
17304                padding: 12px;
17305                background: linear-gradient(135deg, #C9A227 0%, #8B6914 100%);
17306                color: white;
17307                border: none;
17308                border-radius: 8px;
17309                cursor: pointer;
17310                font-weight: 500;
17311                font-size: 14px;
17312                display: flex;
17313                align-items: center;
17314                justify-content: center;
17315                gap: 8px;
17316                transition: all 0.2s;
17317                flex-shrink: 0;
17318            }
17319            
17320            .chat-new-btn:hover {
17321                transform: translateY(-1px);
17322                box-shadow: 0 4px 8px rgba(201, 162, 39, 0.3);
17323            }
17324            
17325            .chat-conversation-title {
17326                flex: 1;
17327                font-weight: 600;
17328                font-size: 16px;
17329                color: #1f2937;
17330            }
17331            
17332            .chat-messages-container {
17333                flex: 1;
17334                overflow-y: auto;
17335                padding: 16px;
17336                display: flex;
17337                flex-direction: column;
17338                gap: 8px;
17339                min-height: 0;
17340                background: #E5DDD5;
17341            }
17342            
17343            /* Custom Scrollbar for Messages Container */
17344            .chat-messages-container::-webkit-scrollbar {
17345                width: 6px;
17346            }
17347            
17348            .chat-messages-container::-webkit-scrollbar-track {
17349                background: rgba(0, 0, 0, 0.05);
17350            }
17351            
17352            .chat-messages-container::-webkit-scrollbar-thumb {
17353                background: rgba(201, 162, 39, 0.6);
17354                border-radius: 3px;
17355            }
17356            
17357            .chat-messages-container::-webkit-scrollbar-thumb:hover {
17358                background: #C9A227;
17359            }
17360            
17361            /* Firefox */
17362            .chat-messages-container {
17363                scrollbar-width: thin;
17364                scrollbar-color: rgba(201, 162, 39, 0.6) rgba(0, 0, 0, 0.05);
17365            }
17366            
17367            /* WhatsApp Group-like Messages */
17368            .chat-message {
17369                display: flex;
17370                margin-bottom: 4px;
17371                animation: fadeIn 0.2s ease;
17372            }
17373            
17374            @keyframes fadeIn {
17375                from { opacity: 0; transform: translateY(10px); }
17376                to { opacity: 1; transform: translateY(0); }
17377            }
17378            
17379            .chat-message.mine {
17380                justify-content: flex-end;
17381            }
17382            
17383            .chat-message-bubble {
17384                max-width: 80%;
17385                padding: 6px 8px 8px 10px;
17386                border-radius: 8px;
17387                position: relative;
17388                box-shadow: 0 1px 0.5px rgba(0,0,0,0.13);
17389            }
17390            
17391            /* Bubble from others (Admin/CS) */
17392            .chat-message:not(.mine) .chat-message-bubble {
17393                background: #FFFFFF;
17394                border-radius: 0 8px 8px 8px;
17395            }
17396            
17397            /* Bubble tail for others */
17398            .chat-message:not(.mine) .chat-message-bubble::before {
17399                content: '';
17400                position: absolute;
17401                top: 0;
17402                left: -8px;
17403                width: 0;
17404                height: 0;
17405                border-style: solid;
17406                border-width: 0 8px 13px 0;
17407                border-color: transparent #FFFFFF transparent transparent;
17408            }
17409            
17410            /* Bubble from user (mine) */
17411            .chat-message.mine .chat-message-bubble {
17412                background: #e4f5d0; /* Light blue for user messages */
17413                border-radius: 8px 0 8px 8px;
17414            }
17415            
17416            /* Bubble tail for mine */
17417            .chat-message.mine .chat-message-bubble::before {
17418                content: '';
17419                position: absolute;
17420                top: 0;
17421                right: -8px;
17422                width: 0;
17423                height: 0;
17424                border-style: solid;
17425                border-width: 0 0 13px 8px;
17426                border-color: transparent transparent transparent #eaf5d0;
17427            }
17428            
17429            .message-sender {
17430                font-size: 12.5px;
17431                font-weight: 600;
17432                color: #C9A227 !important;
17433                margin-bottom: 4px;
17434            }
17435            
17436            .chat-message.mine .message-sender {
17437                display: none;
17438            }
17439            
17440            /* Message text */
17441            .message-text {
17442                font-size: 14px;
17443                line-height: 19px;
17444                color: #ffffff !important;
17445                word-wrap: break-word;
17446                white-space: pre-wrap;
17447                margin-bottom: 4px;
17448            }
17449            
17450            /* Message metadata (time + status) */
17451            .message-meta {
17452                display: flex;
17453                align-items: center;
17454                justify-content: flex-end;
17455                gap: 4px;
17456                margin-top: 2px;
17457            }
17458            
17459            .message-time {
17460                font-size: 11px;
17461                color: #728166 !important;
17462                line-height: 15px;
17463            }
17464            
17465            /* Use multiple selectors to override global styles */
17466            .chat-message-bubble .message-status,
17467            .message-meta .message-status,
17468            .chat-message .message-status {
17469                width: 16px !important;
17470                height: 16px !important;
17471                min-width: 16px !important;
17472                min-height: 16px !important;
17473                max-width: 16px !important;
17474                max-height: 16px !important;
17475                flex-shrink: 0;
17476                color: #728166 !important;
17477                fill: currentColor !important;
17478            }
17479            
17480            .chat-message:not(.mine) .message-status {
17481                display: none;
17482            }
17483            
17484            /* Loading dots animation */
17485            @keyframes loadingDots {
17486                0%, 20% { content: '.'; }
17487                40% { content: '..'; }
17488                60%, 100% { content: '...'; }
17489            }
17490            
17491            .message-status.loading::after {
17492                content: '...';
17493                animation: loadingDots 1.5s infinite;
17494                font-size: 16px;
17495                line-height: 16px;
17496            }
17497            
17498            .message-status.loading path {
17499                display: none;
17500            }
17501            
17502            .message-status.failed {
17503                color: #ef4444 !important;
17504                cursor: pointer;
17505            }
17506            
17507            .message-status.sent {
17508                color: #667781 !important;
17509            }
17510            
17511            .chat-new-form {
17512                flex: 1;
17513                overflow-y: auto;
17514                padding: 16px;
17515                display: flex;
17516                flex-direction: column;
17517            }
17518            
17519            .chat-new-form form {
17520                display: flex;
17521                flex-direction: column;
17522                gap: 16px;
17523                flex: 1;
17524            }
17525            
17526            .chat-new-form .form-group {
17527                display: flex;
17528                flex-direction: column;
17529                gap: 6px;
17530            }
17531            
17532            .chat-new-form label {
17533                font-size: 14px;
17534                font-weight: 500;
17535                color: #374151;
17536            }
17537            
17538            .chat-new-form .form-control {
17539                width: 100%;
17540                padding: 10px 12px;
17541                border: 1px solid #d1d5db;
17542                border-radius: 8px;
17543                font-size: 14px;
17544                font-family: inherit;
17545                transition: border-color 0.2s;
17546                box-sizing: border-box;
17547            }
17548            
17549            .chat-new-form .form-control:focus {
17550                outline: none;
17551                border-color: #C9A227;
17552                box-shadow: 0 0 0 3px rgba(201, 162, 39, 0.1);
17553            }
17554            
17555            .chat-new-form textarea.form-control {
17556                resize: vertical;
17557                min-height: 100px;
17558            }
17559            
17560            .chat-new-form .btn {
17561                padding: 12px 20px;
17562                border: none;
17563                border-radius: 8px;
17564                font-size: 15px;
17565                font-weight: 500;
17566                cursor: pointer;
17567                transition: all 0.2s;
17568                text-align: center;
17569            }
17570            
17571            .chat-new-form .btn-primary {
17572                background: linear-gradient(135deg, #C9A227 0%, #8B6914 100%);
17573                color: white;
17574            }
17575            
17576            .chat-new-form .btn-primary:hover {
17577                transform: translateY(-1px);
17578                box-shadow: 0 4px 8px rgba(201, 162, 39, 0.3);
17579            }
17580            
17581            .chat-new-form .btn-primary:active {
17582                transform: scale(0.98);
17583            }
17584            
17585            .chat-new-form .btn-block {
17586                width: 100%;
17587                margin-top: auto;
17588            }
17589            
17590            .chat-message-form {
17591                padding: 12px;
17592                border-top: 1px solid #e5e7eb;
17593                display: flex;
17594                flex-wrap: wrap;
17595                gap: 8px;
17596                align-items: flex-end;
17597                flex-shrink: 0;
17598                background: white;
17599            }
17600            
17601            .chat-product-card {
17602                flex: 1 1 100%;
17603                max-width: 100%;
17604                display: flex;
17605                gap: 12px;
17606                padding: 12px;
17607                background: #f3f4f6;
17608                border-radius: 8px;
17609                margin-bottom: 4px;
17610                border: 1px solid #e5e7eb;
17611            }
17612            
17613            .chat-product-card img {
17614                width: 60px;
17615                height: 60px;
17616                object-fit: cover;
17617                border-radius: 6px;
17618                flex-shrink: 0;
17619            }
17620            
17621            .chat-product-card-content {
17622                flex: 1;
17623                min-width: 0;
17624            }
17625            
17626            .chat-product-card-name {
17627                font-weight: 500;
17628                font-size: 14px;
17629                color: #1f2937;
17630                margin-bottom: 4px;
17631                white-space: nowrap;
17632                overflow: hidden;
17633                text-overflow: ellipsis;
17634            }
17635            
17636            .chat-product-card-variant {
17637                font-size: 12px;
17638                color: #74806b;
17639                margin-bottom: 4px;
17640            }
17641            
17642            .chat-product-card-price {
17643                font-size: 13px;
17644                color: #8bea66;
17645                font-weight: 600;
17646            }
17647            
17648            .chat-product-card-remove {
17649                align-self: flex-start;
17650                background: none;
17651                border: none;
17652                color: #9ca3af;
17653                cursor: pointer;
17654                padding: 4px;
17655                font-size: 18px;
17656                line-height: 1;
17657                transition: color 0.2s;
17658            }
17659            
17660            .chat-product-card-remove:hover {
17661                color: #ef4444;
17662            }
17663            
17664            /* Product card in messages (read-only display) */
17665            .message-product-card {
17666                display: flex;
17667                gap: 10px;
17668                padding: 10px;
17669                background: rgba(255, 255, 255, 0.5);
17670                border-radius: 8px;
17671                margin-bottom: 6px;
17672                border: 1px solid rgba(0, 0, 0, 0.08);
17673                align-items: center;
17674            }
17675            
17676            .chat-message.mine .message-product-card {
17677                background: rgba(255, 255, 255, 0.7);
17678            }
17679            
17680            .message-product-card img {
17681                width: 50px;
17682                height: 50px;
17683                object-fit: cover;
17684                border-radius: 6px;
17685                flex-shrink: 0;
17686            }
17687            
17688            .message-product-info {
17689                flex: 1;
17690                min-width: 0;
17691            }
17692            
17693            .message-product-name {
17694                font-weight: 500;
17695                font-size: 13px;
17696                color: #33371f;
17697                margin-bottom: 2px;
17698                overflow: hidden;
17699                text-overflow: ellipsis;
17700                display: -webkit-box;
17701                -webkit-line-clamp: 2;
17702                -webkit-box-orient: vertical;
17703                line-height: 1.3;
17704            }
17705            
17706            .message-product-variant {
17707                font-size: 11px;
17708                color: #78806b;
17709                margin-bottom: 2px;
17710            }
17711            
17712            .message-product-price {
17713                font-size: 12px;
17714                color: #c7ea66;
17715                font-weight: 600;
17716            }
17717            
17718            .message-product-link {
17719                flex-shrink: 0;
17720                width: 32px;
17721                height: 32px;
17722                display: flex;
17723                align-items: center;
17724                justify-content: center;
17725                background: rgba(102, 126, 234, 0.1);
17726                border-radius: 6px;
17727                color: #c7ea66;
17728                text-decoration: none;
17729                transition: all 0.2s;
17730            }
17731            
17732            .message-product-link:hover {
17733                background: #b7ea66;
17734                color: white;
17735            }
17736            
17737            .message-product-link svg {
17738                width: 16px;
17739                height: 16px;
17740            }
17741            
17742            .chat-message-input {
17743                flex: 1;
17744                border: 1px solid #d1d5db;
17745                border-radius: 20px;
17746                padding: 10px 16px;
17747                resize: none;
17748                max-height: 100px;
17749                font-family: inherit;
17750                font-size: 14px;
17751                line-height: 1.5;
17752                box-sizing: border-box;
17753                overflow-y: hidden;
17754            }
17755            
17756            .chat-message-input:focus {
17757                outline: none;
17758                border-color: #C9A227;
17759                box-shadow: 0 0 0 3px rgba(201, 162, 39, 0.1);
17760            }
17761            
17762            .chat-send-btn {
17763                width: 40px;
17764                height: 40px;
17765                border-radius: 50%;
17766                background: linear-gradient(135deg, #C9A227 0%, #8B6914 100%);
17767                border: none;
17768                color: white;
17769                cursor: pointer;
17770                flex-shrink: 0;
17771                display: flex;
17772                align-items: center;
17773                justify-content: center;
17774                transition: all 0.2s;
17775            }
17776            
17777            .chat-send-btn:hover {
17778                transform: scale(1.05);
17779                box-shadow: 0 2px 8px rgba(201, 162, 39, 0.3);
17780            }
17781            
17782            .chat-send-btn:active {
17783                transform: scale(0.95);
17784            }
17785            
17786            .chat-loading {
17787                text-align: center;
17788                padding: 20px;
17789                color: #6b7280;
17790            }
17791            
17792            /* Date Separator (WhatsApp-style) */
17793            .date-separator {
17794                display: flex;
17795                align-items: center;
17796                gap: 12px;
17797                margin: 24px 0 16px;
17798                opacity: 0;
17799                animation: fadeInSeparator 0.3s ease forwards;
17800            }
17801
17802            @keyframes fadeInSeparator {
17803                from {
17804                    opacity: 0;
17805                    transform: translateY(-10px);
17806                }
17807                to {
17808                    opacity: 1;
17809                    transform: translateY(0);
17810                }
17811            }
17812
17813            .date-separator-line {
17814                flex: 1;
17815                height: 1px;
17816                background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.1) 50%, transparent);
17817            }
17818
17819            .date-separator-text {
17820                flex-shrink: 0;
17821                padding: 4px 12px;
17822                background: #e9ecef;
17823                color: #6c757d;
17824                font-size: 11px;
17825                font-weight: 600;
17826                text-transform: uppercase;
17827                letter-spacing: 0.5px;
17828                border-radius: 12px;
17829                box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
17830            }
17831            
17832            /* Mobile responsive */
17833            @media (max-width: 640px) {
17834                .chat-popup {
17835                    position: fixed !important; /* Override absolute positioning */
17836                    bottom: 45px !important; /* Above bottom nav */
17837                    right: 0 !important;
17838                    left: 0 !important;
17839                    width: 100% !important;
17840                    max-width: 100% !important;
17841                    /* Use dvh (dynamic viewport height) for browser UI awareness */
17842                    height: calc(100dvh - 45px) !important;
17843                    max-height: calc(100dvh - 45px) !important;
17844                    /* Fallback for browsers that don't support dvh */
17845                    height: calc(var(--viewport-height, 100vh) - 45px) !important;
17846                    max-height: calc(var(--viewport-height, 100vh) - 45px) !important;
17847                    border-radius: 0 !important;
17848                    z-index: 9999 !important;
17849                }
17850                
17851                .chat-popup-header {
17852                    border-radius: 0;
17853                }
17854                
17855                .chat-messages-container,
17856                .chat-conversations-list {
17857                    /* Adjust for mobile keyboard using dynamic viewport */
17858                    max-height: calc(100dvh - 190px);
17859                    /* Fallback for browsers that don't support dvh */
17860                    max-height: calc(var(--viewport-height, 100vh) - 190px);
17861                }
17862            }
17863        </style>
17864                <!-- Chat Widget Button -->
17865        <div id="chat-widget" class="chat-widget" 
17866             data-user-id=""
17867             data-is-logged-in="false"
17868             data-csrf-token="df5d776748b1ed8c8dd0d987bae99b1ee3dea6c6dcbe0eedb1bea766c56d569a">
17869            <button id="chat-toggle-btn" class="chat-toggle-btn" aria-label="Toggle chat">
17870                <svg class="chat-icon" width="28" height="28" viewBox="0 0 640 640" fill="currentColor">
17871                    <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 64
17871C443.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"/>
17872                </svg>
17873                <span id="chat-unread-badge" class="chat-unread-badge" style="display: none;">0</span>
17874            </button>
17875            
17876            <div id="chat-popup" class="chat-popup" style="display: none;">
17877                <div class="chat-popup-header">
17878                    <div class="chat-header-title">
17879                        <h3>Bantuan</h3>
17880                        <span id="chatOperatingHours" class="chat-operating-hours" style="display: none;"></span>
17881                    </div>
17882                    <div class="chat-header-actions">
17883                        <button id="chatNotifToggle" class="btn-notif-toggle" data-status="not-supported" title="Push notification" style="display: none;">
17884                            <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
17885                                <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"/>
17886                            </svg>
17887                        </button>
17888                        <button id="chat-close-btn" class="chat-close-btn" aria-label="Close chat">×</button>
17889                    </div>
17890                </div>
17891                
17892                <!-- Notification Info Bar (conditional) -->
17893                <div class="chat-notif-info-bar" id="chatNotifInfoBar" style="display:none;">
17894                    <svg width="14" height="14" viewBox="0 0 24 24" fill="#EAB308">
17895                        <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"/>
17896                    </svg>
17897                    <span class="small">Aktifkan notifikasi untuk pemberitahuan balasan</span>
17898                    <button class="btn-dismiss" onclick="ChatNotification.dismissInfo()">✕</button>
17899                </div>
17900                
17901                <div class="chat-popup-body">
17902                    <!-- Conversations List View -->
17903                    <div id="chat-list-view" class="chat-view">
17904                        <div id="chat-conversations-list" class="chat-conversations-list">
17905                            <div class="chat-loading">Memuat...</div>
17906                            <!-- Conversation Item Structure (rendered by JS):
17907                            <div class="chat-conversation-item">
17908                                <div class="conversation-avatar">
17909                                    <svg>...</svg>
17910                                </div>
17911                                <div class="conversation-content">
17912                                    <div class="conversation-header">
17913                                        <span class="conversation-name">Admin Support</span>
17914                                        <span class="conversation-time">10:30</span>
17915                                    </div>
17916                                    <div class="conversation-preview">
17917                                        <p class="conversation-message">Terima kasih telah menghubungi kami...</p>
17918                                        <span class="conversation-unread">2</span>
17919                                    </div>
17920                                </div>
17921                            </div>
17922                            -->
17923                        </div>
17924                        <button id="chat-new-btn" class="chat-new-btn">
17925                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
17926                                <line x1="12" y1="5" x2="12" y2="19"></line>
17927                                <line x1="5" y1="12" x2="19" y2="12"></line>
17928                            </svg>
17929                            Percakapan Baru
17930                        </button>
17931                    </div>
17932                    
17933                    <!-- Chat Messages View -->
17934                    <div id="chat-messages-view" class="chat-view" style="display: none;">
17935                        <div id="chat-messages-container" class="chat-messages-container">
17936                            <div class="chat-loading">Memuat pesan...</div>
17937                            <!-- Message Structure (rendered by JS):
17938                            <div class="chat-message"> (or .chat-message.mine)
17939                                <div class="chat-message-bubble">
17940                                    <div class="message-sender">Admin Support</div> (only for non-mine)
17941                                    <div class="message-text">Halo, ada yang bisa dibantu?</div>
17942                                    <div class="message-meta">
17943                                        <span class="message-time">10:30</span>
17944                                        <svg class="message-status">...</svg> (only for mine)
17945                                    </div>
17946                                </div>
17947                            </div>
17948                            -->
17949                        </div>
17950                        
17951                        <form id="chat-message-form" class="chat-message-form">
17952                            <input type="hidden" id="chat-current-conversation-id" value="">
17953                            <textarea 
17954                                id="chat-message-input" 
17955                                class="chat-message-input" 
17956                                placeholder="Ketik pesan..."
17957                                rows="1"
17958                                maxlength="5000"
17959                            ></textarea>
17960                            <button type="submit" class="chat-send-btn" aria-label="Send message">
17961                                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
17962                                    <line x1="22" y1="2" x2="11" y2="13"></line>
17963                                    <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
17964                                </svg>
17965                            </button>
17966                        </form>
17967                    </div>
17968                    
17969                    <!-- New Chat View -->
17970                    <div id="chat-new-view" class="chat-view" style="display: none;">
17971                        <div class="chat-messages-header">
17972                            <button id="chat-new-back-btn" class="chat-back-btn" aria-label="Back to list">
17973                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
17974                                    <path d="M19 12H5M12 19l-7-7 7-7"/>
17975                                </svg>
17976                            </button>
17977                            <div class="chat-conversation-title">Percakapan Baru</div>
17978                        </div>
17979                        
17980                        <div class="chat-new-form">
17981                            <form id="chat-start-form">
17982                                <div class="form-group">
17983                                    <label for="chat-new-subject">Subjek</label>
17984                                    <input 
17985                                        type="text" 
17986                                        id="chat-new-subject" 
17987                                        class="form-control" 
17988                                        placeholder="Bantuan Pelanggan"
17989                                        maxlength="255"
17990                                    >
17991                                </div>
17992                                <div class="form-group">
17993                                    <label for="chat-new-message">Pesan</label>
17994                                    <textarea 
17995                                        id="chat-new-message" 
17996                                        class="form-control" 
17997                                        rows="4" 
17998                                        placeholder="Tulis pertanyaan Anda..."
17999                                        required
18000                                        maxlength="5000"
18001                                    ></textarea>
18002                                </div>
18003                                <button type="submit" class="btn btn-primary btn-block">Kirim</button>
18004                            </form>
18005                        </div>
18006                    </div>
18007                </div>
18008            </div>
18009        </div>
18010        
18011        <!-- Login Required Modal -->
18012        <div class="modal-overlay" id="chatLoginModal">
18013            <div class="modal-dialog">
18014                <div class="modal-header">
18015                    <h3 class="modal-title">
18016                        <div class="modal-icon info">
18017                            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18018                                <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"/>
18019                            </svg>
18020                        </div>
18021                        Login Diperlukan
18022                    </h3>
18023                </div>
18024                <div class="modal-body">
18025                    <p class="modal-message">
18025Anda harus login terlebih dahulu untuk menggunakan fitur chat bantuan pelanggan.</p>
18026                    <div class="modal-actions">
18027                        <button class="modal-btn modal-btn-secondary" id="chatLoginCancel">Batal</button>
18028                        <button class="modal-btn modal-btn-primary" id="chatLoginConfirm">Login Sekarang</button>
18029                    </div>
18030                </div>
18031            </div>
18032        </div>
18033        
18033<script src="https://plazait.co.id/assets/js/chat-widget.js" nonce="se3iyzEHMc9FQEB4wSyd0w=="></script>
18033<script data-inlined="chat-notification.js">
18034/**
18035 * Customer Chat Push Notification Manager
18036 * Handles browser push notification subscription for customers
18037 * Version: 1.0.0
18038 * Date: 2026-01-19
18039 */
18040
18041const ChatNotification = {
18042    // State
18043    status: 'not-supported', // not-supported, not-subscribed, subscribed, blocked
18044    subscription: null,
18045    vapidPublicKey: null, // Will be loaded from server
18046    
18047    /**
18048     * Initialize notification manager
18049     */
18050    async init() {
18051        // Check if user is logged in (only for logged-in users)
18052        if (!this.isUserLoggedIn()) {
18053            this.updateUI('not-supported');
18054            return;
18055        }
18056        
18057        // Check browser support
18058        if (!this.isSupported()) {
18059            this.updateUI('not-supported');
18060            return;
18061        }
18062        
18063        // Load VAPID key from server
18064        await this.loadVapidKey();
18065        
18066        if (!this.vapidPublicKey) {
18067            this.updateUI('not-supported');
18068            return;
18069        }
18070        
18071        // Register service worker if needed
18072        await this.registerServiceWorker();
18073        
18074        // Check current permission
18075        const permission = Notification.permission;
18076        
18077        if (permission === 'denied') {
18078            this.updateUI('blocked');
18079        } else if (permission === 'granted') {
18080            // Check if already subscribed
18081            const isSubscribed = await this.checkSubscriptionStatus();
18082            this.updateUI(isSubscribed ? 'subscribed' : 'not-subscribed');
18083        } else {
18084            // Default: not subscribed yet
18085            this.updateUI('not-subscribed');
18086            this.showInfoBar();
18087        }
18088    },
18089    
18090    /**
18091     * Check if user is logged in
18092     */
18093    isUserLoggedIn() {
18094        const metaTag = document.querySelector('meta[name="user-logged-in"]');
18095        return metaTag ? metaTag.getAttribute('content') === 'true' : false;
18096    },
18097    
18098    /**
18099     * Check if browser supports push notifications
18100     */
18101    isSupported() {
18102        return (
18103            'serviceWorker' in navigator &&
18104            'PushManager' in window &&
18105            'Notification' in window
18106        );
18107    },
18108    
18109    /**
18110     * Load VAPID public key from server
18111     */
18112    async loadVapidKey() {
18113        try {
18114            const response = await fetch('/api/chat/notifications/vapid_key', {
18115                method: 'GET',
18116                headers: {
18117                    'Accept': 'application/json'
18118                }
18119            });
18120            
18121            if (!response.ok) {
18122                return false;
18123            }
18124            
18125            const data = await response.json();
18126            if (data.success && data.data?.public_key) {
18127                this.vapidPublicKey = data.data.public_key;
18128                return true;
18129            }
18130            
18131            return false;
18132            
18133        } catch (error) {
18134            console.error('[ChatNotification] Error loading VAPID key:', error);
18135            return false;
18136        }
18137    },
18138    
18139    /**
18140     * Register service worker
18141     */
18142    async registerServiceWorker() {
18143        if (!('serviceWorker' in navigator)) return null;
18144        
18145        try {
18146            const registration = await navigator.serviceWorker.register('/sw-push.js', {
18147                scope: '/'
18148            });
18149            return registration;
18150        } catch (error) {
18151            return null;
18152        }
18153    },
18154    
18155    /**
18156     * Check subscription status from server
18157     */
18158    async checkSubscriptionStatus() {
18159        try {
18160            const response = await fetch('/api/chat/notifications/status', {
18161                method: 'GET',
18162                credentials: 'include',
18163                headers: {
18164                    'Accept': 'application/json'
18165                }
18166            });
18167            
18168            if (!response.ok) {
18169                return false;
18170            }
18171            
18172            const data = await response.json();
18173            return data.data?.subscribed || false;
18174            
18175        } catch (error) {
18176            console.error('[ChatNotification] Error checking subscription:', error);
18177            return false;
18178        }
18179    },
18180    
18181    /**
18182     * Update UI based on status
18183     */
18184    updateUI(status) {
18185        this.status = status;
18186        const btn = document.getElementById('chatNotifToggle');
18187        
18188        if (!btn) {
18189            // Retry after a short delay (button might not be in DOM yet)
18190            setTimeout(() => this.updateUI(status), 100);
18191            return;
18192        }
18193        
18194        btn.setAttribute('data-status', status);
18195        
18196        // Show/hide button based on support
18197        if (status === 'not-supported') {
18198            btn.style.display = 'none';
18199        } else {
18200            btn.style.display = 'block';
18201        }
18202        
18203        // Update SVG icon (bell vs bell-slash)
18204        const svg = btn.querySelector('svg');
18205        if (svg) {
18206            if (status === 'subscribed') {
18207                // Bell icon (active)
18208                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"/>';
18209            } else {
18210                // Bell slash icon (inactive/blocked)
18211                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"/>';
18212            }
18213        }
18214        
18215        // Update tooltip
18216        const tooltips = {
18217            'not-subscribed': 'Klik untuk aktifkan notifikasi',
18218            'subscribed': 'Notifikasi aktif',
18219            'blocked': 'Notifikasi diblokir'
18220        };
18221        
18222        if (tooltips[status]) {
18223            btn.title = tooltips[status];
18224        }
18225        
18226        // Hide info bar if subscribed or blocked
18227        if (status === 'subscribed' || status === 'blocked') {
18228            this.hideInfoBar();
18229        }
18230    },
18231    
18232    /**
18233     * Show info bar (if not dismissed)
18234     */
18235    showInfoBar() {
18236        if (localStorage.getItem('chat_notif_info_dismissed')) {
18237            return;
18238        }
18239        
18240        const infoBar = document.getElementById('chatNotifInfoBar');
18241        const popupBody = document.getElementById('chat-popup')?.querySelector('.chat-popup-body');
18242        
18243        if (infoBar) {
18244            infoBar.style.display = 'flex';
18245
18246            // Bind dismiss button here so it's always attached when bar is visible
18247            const btn = infoBar.querySelector('#chatNotifDismissBtn');
18248            if (btn && !btn._dismissBound) {
18249                btn._dismissBound = true;
18250                btn.addEventListener('click', () => {
18251                    infoBar.style.display = 'none';
18252                    localStorage.setItem('chat_notif_info_dismissed', '1');
18253                    if (popupBody) popupBody.classList.remove('has-info-bar');
18254                });
18255            }
18256        }
18257        
18258        if (popupBody) {
18259            popupBody.classList.add('has-info-bar');
18260        }
18261    },
18262    
18263    /**
18264     * Hide info bar
18265     */
18266    hideInfoBar() {
18267        const infoBar = document.getElementById('chatNotifInfoBar');
18268        const popupBody = document.getElementById('chat-popup')?.querySelector('.chat-popup-body');
18269        
18270        if (infoBar) {
18271            infoBar.style.display = 'none';
18272        }
18273        
18274        if (popupBody) {
18275            popupBody.classList.remove('has-info-bar');
18276        }
18277    },
18278    
18279    /**
18280     * Dismiss info bar permanently
18281     */
18282    dismissInfo() {
18283        this.hideInfoBar();
18284        localStorage.setItem('chat_notif_info_dismissed', '1');
18285    },
18286    
18287    /**
18288     * Handle bell icon click
18289     */
18290    async handleToggleClick() {
18291        if (this.status === 'not-subscribed') {
18292            await this.subscribe();
18293        } else if (this.status === 'subscribed') {
18294            await this.unsubscribe();
18295        } else if (this.status === 'blocked') {
18296            this.showUnblockHelp();
18297        }
18298    },
18299    
18300    /**
18301     * Subscribe to push notifications
18302     */
18303    async subscribe() {
18304        try {
18305            // Request permission
18306            const permission = await Notification.requestPermission();
18307            
18308            if (permission === 'denied') {
18309                this.updateUI('blocked');
18310                this.showToast('Notifikasi diblokir', 'error');
18311                return;
18312            }
18313            
18314            if (permission !== 'granted') {
18315                return;
18316            }
18317            
18318            // Get service worker registration
18319            const registration = await navigator.serviceWorker.ready;
18320            
18321            // Subscribe to push
18322            const subscription = await registration.pushManager.subscribe({
18323                userVisibleOnly: true,
18324                applicationServerKey: this.urlBase64ToUint8Array(this.vapidPublicKey)
18325            });
18326            
18327            // Send subscription to server
18328            const response = await fetch('/api/chat/notifications/subscribe', {
18329                method: 'POST',
18330                credentials: 'include',
18331                headers: {
18332                    'Content-Type': 'application/json',
18333                    'Accept': 'application/json'
18334                },
18335                body: JSON.stringify(subscription.toJSON())
18336            });
18337            
18338            if (!response.ok) {
18339                throw new Error('Failed to save subscription');
18340            }
18341            
18342            const result = await response.json();
18343            
18344            this.subscription = subscription;
18345            this.updateUI('subscribed');
18346            this.hideInfoBar();
18347            this.showToast('Notifikasi berhasil diaktifkan!', 'success');
18348            
18349            // Send test notification
18350            setTimeout(() => this.sendTestNotification(), 1000);
18351            
18352        } catch (error) {
18353            console.error('[ChatNotification] Subscribe error:', error);
18354            this.showToast('Gagal mengaktifkan notifikasi', 'error');
18355        }
18356    },
18357    
18358    /**
18359     * Unsubscribe from push notifications
18360     */
18361    async unsubscribe() {
18362        try {
18363            // Confirm first
18364            if (!confirm('Nonaktifkan notifikasi chat?')) {
18365                return;
18366            }
18367            
18368            // Get current subscription
18369            const registration = await navigator.serviceWorker.ready;
18370            const subscription = await registration.pushManager.getSubscription();
18371            
18372            if (subscription) {
18373                // Unsubscribe from browser
18374                await subscription.unsubscribe();
18375                
18376                // Tell server
18377                await fetch('/api/chat/notifications/unsubscribe', {
18378                    method: 'POST',
18379                    credentials: 'include',
18380                    headers: {
18381                        'Content-Type': 'application/json',
18382                        'Accept': 'application/json'
18383                    },
18384                    body: JSON.stringify(subscription.toJSON())
18385                });
18386            }
18387            
18388            this.subscription = null;
18389            this.updateUI('not-subscribed');
18390            this.showInfoBar();
18391            this.showToast('Notifikasi dinonaktifkan', 'info');
18392            
18393        } catch (error) {
18394            console.error('[ChatNotification] Unsubscribe error:', error);
18395            this.showToast('Gagal menonaktifkan notifikasi', 'error');
18396        }
18397    },
18398    
18399    /**
18400     * Send test notification
18401     */
18402    async sendTestNotification() {
18403        try {
18404            const response = await fetch('/api/chat/notifications/send_test', {
18405                method: 'POST',
18406                credentials: 'include',
18407                headers: {
18408                    'Accept': 'application/json'
18409                }
18410            });
18411            
18412            if (response.ok) {
18413                // Test notification sent successfully
18414            }
18415        } catch (error) {
18416            // Test notification failed silently
18417        }
18418    },
18419    
18420    /**
18421     * Show unblock help modal/message
18422     */
18423    showUnblockHelp() {
18424        const message = `
18425            Notifikasi diblokir oleh browser.\n\n
18426            Untuk mengaktifkan:\n
18427            1. Klik ikon gembok/info di address bar\n
18428            2. Cari "Notifications" atau "Notifikasi"\n
18429            3. Ubah menjadi "Allow" atau "Izinkan"\n
18430            4. Refresh halaman ini
18431        `.trim().replace(/\s+/g, ' ');
18432        
18433        alert(message);
18434    },
18435    
18436    /**
18437     * Show toast notification
18438     */
18439    showToast(message, type = 'info') {
18440        // Simple toast implementation
18441        const toast = document.createElement('div');
18442        toast.className = `chat-toast chat-toast-${type}`;
18443        toast.textContent = message;
18444        toast.style.cssText = `
18445            position: fixed;
18446            bottom: 80px;
18447            right: 20px;
18448            background: ${type === 'success' ? '#4caf50' : type === 'error' ? '#f44336' : '#2196f3'};
18449            color: white;
18450            padding: 12px 20px;
18451            border-radius: 4px;
18452            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
18453            z-index: 10000;
18454            animation: slideInUp 0.3s ease;
18455        `;
18456        
18457        document.body.appendChild(toast);
18458        
18459        setTimeout(() => {
18460            toast.style.animation = 'slideOutDown 0.3s ease';
18461            setTimeout(() => toast.remove(), 300);
18462        }, 3000);
18463    },
18464    
18465    /**
18466     * Convert VAPID key to Uint8Array
18467     */
18468    urlBase64ToUint8Array(base64String) {
18469        const padding = '='.repeat((4 - base64String.length % 4) % 4);
18470        const base64 = (base64String + padding)
18471            .replace(/\-/g, '+')
18472            .replace(/_/g, '/');
18473        
18474        const rawData = window.atob(base64);
18475        const outputArray = new Uint8Array(rawData.length);
18476        
18477        for (let i = 0; i < rawData.length; ++i) {
18478            outputArray[i] = rawData.charCodeAt(i);
18479        }
18480        return outputArray;
18481    },
18482    
18483    /**
18484     * Check if push notification is currently active
18485     * @returns {Promise<boolean>}
18486     */
18487    async isPushActive() {
18488        try {
18489            // Check browser subscription
18490            if (!('serviceWorker' in navigator)) return false;
18491            
18492            const registration = await navigator.serviceWorker.getRegistration();
18493            if (!registration) return false;
18494            
18495            const subscription = await registration.pushManager.getSubscription();
18496            if (!subscription) return false;
18497            
18498            // Also check server status
18499            const serverStatus = await this.checkSubscriptionStatus();
18500            
18501            return serverStatus;
18502        } catch (error) {
18503            return false;
18504        }
18505    },
18506    
18507    /**
18508     * Show logout confirmation if push notification is active
18509     * Call this before logout action
18510     * @returns {Promise<boolean>} true if user confirms logout, false if cancelled
18511     */
18512    async confirmLogoutWithPushNotification() {
18513        const isActive = await this.isPushActive();
18514        
18515        if (!isActive) {
18516            // No active push, proceed with logout
18517            return true;
18518        }
18519        
18520        // Show modal confirmation using modal.css
18521        return new Promise((resolve) => {
18522            // Create modal HTML
18523            const modalHTML = `
18524                <div class="modal-overlay" id="logoutConfirmModal">
18525                    <div class="modal-dialog">
18526                        <div class="modal-header">
18527                            <h3 class="modal-title">
18528                                <span class="modal-icon warning">⚠️</span>
18529                                Konfirmasi Logout
18530                            </h3>
18531                        </div>
18532                        <div class="modal-body">
18533                            <p class="modal-message">
18534                                Anda memiliki <strong>notifikasi push yang aktif</strong>.<br><br>
18535                                Setelah logout, Anda tidak akan menerima notifikasi chat lagi di perangkat ini.<br><br>
18536                                Apakah Anda yakin ingin melanjutkan logout?
18537                            </p>
18538                        </div>
18539                        <div class="modal-actions" style="padding: 0 20px 20px;">
18540                            <button class="modal-btn modal-btn-secondary" id="logoutCancelBtn">
18541                                Batal
18542                            </button>
18543                            <button class="modal-btn modal-btn-danger" id="logoutConfirmBtn">
18544                                Lanjutkan Logout
18545                            </button>
18546                        </div>
18547                    </div>
18548                </div>
18549            `;
18550            
18551            // Append to body
18552            const modalContainer = document.createElement('div');
18553            modalContainer.innerHTML = modalHTML;
18554            document.body.appendChild(modalContainer.firstElementChild);
18555            
18556            const modal = document.getElementById('logoutConfirmModal');
18557            const confirmBtn = document.getElementById('logoutConfirmBtn');
18558            const cancelBtn = document.getElementById('logoutCancelBtn');
18559            
18560            // Show modal with animation
18561            requestAnimationFrame(() => {
18562                modal.classList.add('show');
18563            });
18564            
18565            // Handle confirm
18566            const handleConfirm = () => {
18567                cleanup();
18568                resolve(true);
18569            };
18570            
18571            // Handle cancel
18572            const handleCancel = () => {
18573                cleanup();
18574                resolve(false);
18575            };
18576            
18577            // Cleanup function
18578            const cleanup = () => {
18579                modal.classList.remove('show');
18580                setTimeout(() => {
18581                    modal.remove();
18582                }, 200);
18583                confirmBtn.removeEventListener('click', handleConfirm);
18584                cancelBtn.removeEventListener('click', handleCancel);
18585                modal.removeEventListener('click', handleBackdropClick);
18586            };
18587            
18588            // Handle backdrop click
18589            const handleBackdropClick = (e) => {
18590                if (e.target === modal) {
18591                    handleCancel();
18592                }
18593            };
18594            
18595            // Add event listeners
18596            confirmBtn.addEventListener('click', handleConfirm);
18597            cancelBtn.addEventListener('click', handleCancel);
18598            modal.addEventListener('click', handleBackdropClick);
18599        });
18600    }
18601};
18602
18603// Add CSS animations
18604const chatNotifStyle = document.createElement('style');
18605chatNotifStyle.textContent = `
18606    @keyframes slideInUp {
18607        from {
18608            transform: translateY(100%);
18609            opacity: 0;
18610        }
18611        to {
18612            transform: translateY(0);
18613            opacity: 1;
18614        }
18615    }
18616    
18617    @keyframes slideOutDown {
18618        from {
18619            transform: translateY(0);
18620            opacity: 1;
18621        }
18622        to {
18623            transform: translateY(100%);
18624            opacity: 0;
18625        }
18626    }
18627`;
18628document.head.appendChild(chatNotifStyle);
18629
18630// Auto-initialize when DOM is ready
18631if (document.readyState === 'loading') {
18632    document.addEventListener('DOMContentLoaded', () => ChatNotification.init());
18633} else {
18634    ChatNotification.init();
18635}
18636
18637// Expose to window for access from other scripts
18638window.ChatNotification = ChatNotification;
18639
18640</script>
18640  
18641    <!-- Footer Collapse Toggle Script -->
18642  
18642<script nonce="se3iyzEHMc9FQEB4wSyd0w==">
18643  document.addEventListener('DOMContentLoaded', function() {
18644    const ftTop = document.getElementById('ftTop');
18645    const expandBtn = document.getElementById('ftExpandBtn');
18646    
18647    if (ftTop && expandBtn) {
18648      const expandText = expandBtn.querySelector('.expand-text');
18649      
18650      expandBtn.addEventListener('click', function() {
18651        ftTop.classList.toggle('expanded');
18652        const isExpanded = ftTop.classList.contains('expanded');
18653        if (expandText) {
18654          expandText.textContent = isExpanded ? 'Sembunyikan' : 'Selengkapnya';
18655        }
18656      });
18657    }
18658  });
18659  </script>
18659
18660  </footer>
18661
18662<!-- Compare Sidebar Overlay -->
18663<div class="compare-overlay" id="compareOverlay"></div>
18664
18665<!-- Floating Compare Badge (Sticky) -->
18666<button class="compare-floating-badge" id="compareFloatingBadge" aria-label="Lihat produk yang dibandingkan" style="display:none;">
18667  <svg class="cfb-icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
18668    <path d="M9.01 14H2v2h7.01v3L13 15l-3.99-4v3zm5.98-1v-3H22V8h-7.01V5L11 9l3.99 4z"/>
18669  </svg>
18670  <span class="cfb-text">Bandingkan</span>
18671  <span class="cfb-count" id="cfbCount">0</span>
18672</button>
18673
18674<!-- AI Comparison Modal -->
18675<div class="ai-compare-modal" id="aiCompareModal">
18676  <div class="modal-backdrop"></div>
18677  <div class="modal-container">
18678    <div class="modal-header">
18679      <h3 class="modal-title">
18680        <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
18681          <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"/>
18682        </svg>
18683        Analisis AI: Perbandingan Produk
18684      </h3>
18685      <button class="modal-close" id="aiCompareModalClose" aria-label="Tutup">
18686        <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18687          <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"/>
18688        </svg>
18689      </button>
18690    </div>
18691    
18692    <div class="modal-content">
18693      <!-- Loading State -->
18694      <div class="ai-loading" id="aiLoading">
18695        <div class="loading-animation">
18696          <div class="typing-indicator">
18697            <span></span>
18698            <span></span>
18699            <span></span>
18700          </div>
18701          <p>AI sedang menganalisis produk...</p>
18702          <small>Proses ini membutuhkan waktu 10-30 detik</small>
18703        </div>
18704      </div>
18705      
18706      <!-- Error State -->
18707      <div class="ai-error" id="aiError" hidden>
18708        <div class="error-icon">
18709          <svg width="48" height="48" viewBox="0 0 24 24" fill="#ef4444">
18710            <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"/>
18711          </svg>
18712        </div>
18713        <h4>Analisis AI Gagal</h4>
18714        <p id="aiErrorMessage">Terjadi kesalahan saat memproses analisis.</p>
18715        <button class="btn btn-outline-primary" id="aiRetry">Coba Lagi</button>
18716      </div>
18717      
18718      <!-- Analysis Results -->
18719      <div class="ai-results" id="aiResults" hidden>
18720        <!-- Summary Section -->
18721        <div class="ai-summary-card">
18722          <h4>
18723            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18724              <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"/>
18725            </svg>
18726            Ringkasan Analisis
18727          </h4>
18728          <p id="aiSummary" class="summary-text"></p>
18729          <div class="winner-badge" id="winnerBadge">
18730            <strong>Rekomendasi Terbaik:</strong> <span id="winnerProduct"></span>
18731            <small id="winnerReason"></small>
18732          </div>
18733        </div>
18734        
18735        <!-- Comparison Table -->
18736        <div class="ai-comparison-table">
18737          <h4>
18738            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18739              <path d="M3,3H21V5H3V3M3,7H21V9H3V7M3,11H21V13H3V11M3,15H21V17H3V15M3,19H21V21H3V19Z"/>
18740            </svg>
18741            Perbandingan Detail
18742          </h4>
18743          <div id="comparisonTable" class="comparison-grid"></div>
18744        </div>
18745        
18746        <!-- Detailed Analysis -->
18747        <div class="ai-detailed-analysis">
18748          <h4>
18749            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18750              <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"/>
18751            </svg>
18752            Analisis Mendalam
18753          </h4>
18754          <div id="detailedAnalysis" class="products-analysis"></div>
18755        </div>
18756        
18757        <!-- Recommendations -->
18758        <div class="ai-recommendations">
18759          <h4>
18760            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18761              <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"/>
18762            </svg>
18763            Rekomendasi Berdasarkan Kebutuhan
18764          </h4>
18765          <div class="recommendations-grid">
18766            <div class="recommendation-card">
18767              <h5>💰 Budget Conscious</h5>
18768              <p id="budgetRecommendation"></p>
18769            </div>
18770            <div class="recommendation-card">
18771              <h5>⚡ Performance Focused</h5>
18772              <p id="performanceRecommendation"></p>
18773            </div>
18774            <div class="recommendation-card">
18775              <h5>⚖️ Balanced Choice</h5>
18776              <p id="balancedRecommendation"></p>
18777            </div>
18778          </div>
18779        </div>
18780        
18781        <!-- Buying Tips -->
18782        <div class="ai-tips">
18783          <h4>
18784            <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18785              <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
18785M5,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"/>
18786            </svg>
18787            Tips Pembelian
18788          </h4>
18789          <ul id="buyingTips" class="tips-list"></ul>
18790        </div>
18791      </div>
18792    </div>
18793  </div>
18794</div>
18795
18796<!-- Cart Login Modal -->
18797<div class="modal-overlay" id="cartLoginModal">
18798  <div class="modal-dialog">
18799    <div class="modal-header">
18800      <h3 class="modal-title">
18801        <div class="modal-icon info">
18802          <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
18803            <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"/>
18804          </svg>
18805        </div>
18806        Login untuk Melanjutkan
18807      </h3>
18808    </div>
18809    <div class="modal-body">
18810      <p class="modal-message" style="margin-bottom: 16px;">Silakan login atau daftar untuk mengakses keranjang belanja dan menyelesaikan pesanan Anda.</p>
18811      <div style="background: #f8fafc; border-radius: 8px; padding: 12px; margin-bottom: 20px;">
18812        <p style="font-size: 14px; color: #646947; margin: 0; line-height: 1.6;">
18813          <strong style="color: #2a2a0f;">Keuntungan dengan akun:</strong><br>
18814          ✓ Simpan keranjang belanja Anda<br>
18815          ✓ Lacak status pesanan secara real-time<br>
18816          ✓ Checkout lebih cepat dengan data tersimpan<br>
18817          ✓ Dapatkan notifikasi promo & diskon eksklusif
18818        </p>
18819      </div>
18820      <div class="modal-actions">
18821        <button class="modal-btn modal-btn-secondary" id="cartLoginCancel">Batal</button>
18822        <button class="modal-btn modal-btn-primary" id="cartLoginConfirm" style="background: #ff6b35; border-color: #ff6b35;">Login</button>
18823        <button class="modal-btn modal-btn-primary" id="cartRegisterBtn" style="background: #f7931e; border-color: #f7931e;">Daftar</button>
18824      </div>
18825    </div>
18826  </div>
18827</div>
18828
18829
18830<script data-inlined="app.js">
18831(function initLiveSearch(){
18832  function setup() {
18833    const searchInput = document.getElementById('searchInput');
18834    const suggest = document.getElementById('searchSuggest');
18835    const searchBtn = document.getElementById('searchBtn');
18836    const aiToggleBtn = document.getElementById('aiToggleBtn');
18837    
18838    if (!searchInput || !suggest || !aiToggleBtn) {
18839      console.log('[LiveSearch] Elemen belum ada, retry...');
18840      return false;
18841    }
18842
18843    let debounceTimer = null;
18844    let lastQuery = '';
18845    let currentIndex = -1;
18846    let currentAbortController = null;
18847    let isAIModeEnabled = false; // AI mode OFF by default
18848    let isAIProcessing = false; // Flag to prevent closing suggest box during AI processing
18849    
18850    // Trending cache
18851    let trendingCache = null;
18852    let trendingLoading = false;
18853    
18854    // Trending products cache
18855    let trendingProductsCache = null;
18856    let trendingProductsLoading = false;
18857    
18858    const MIN_LEN = 2;
18859    const DEBOUNCE = 300;
18860    const AI_TIMEOUT = 15000; // 15 seconds timeout for AI endpoint
18861
18862    // Load AI mode preference from localStorage
18863    const savedAIMode = localStorage.getItem('plazait_ai_search_mode');
18864    if (savedAIMode === 'enabled') {
18865      isAIModeEnabled = false;
18866      aiToggleBtn.classList.remove('active');
18867    }
18868    
18869    // Update placeholder based on AI mode
18870    function updatePlaceholder() {
18871      if (isAIModeEnabled) {
18872        searchInput.placeholder = '🤖 Coba: "laptop HP core i3 untuk kerja" atau "tinta epson l3210"';
18873      } else {
18874        // Static placeholder for typing animation
18875        searchInput.placeholder = '';
18876      }
18877    }
18878    
18879    // Typing animation for placeholder
18880    let typingTimeout = null;
18881    let currentProductIndex = 0;
18882    let currentCharIndex = 0;
18883    let isTyping = false;
18884    let isDeleting = false;
18885    
18886    const popularProducts = [
18887      'Oppo A6',
18888      'Asus Ryzen 3',
18889      'Starlink V4',
18890      'HP Victus',
18891      'Lenovo RTX 5060',
18892      'ROG Strix',
18893      'iPhone 13',
18894      'Xpad 20',
18895      'Xiaomi 14',
18896      'Epson L3210',
18897      'Realme C85',
18898      'Brother T720',
18899      'Mouse Logitech'
18900    ];
18901    
18902    function typeWriter() {
18903      if (isAIModeEnabled || document.activeElement === searchInput) {
18904        return; // Skip typing if AI mode is on or input is focused
18905      }
18906      
18907      const currentProduct = popularProducts[currentProductIndex];
18908      
18909      if (!isDeleting) {
18910        // Typing forward
18911        if (currentCharIndex < currentProduct.length) {
18912          searchInput.placeholder = currentProduct.substring(0, currentCharIndex + 1);
18913          currentCharIndex++;
18914          typingTimeout = setTimeout(typeWriter, 100); // Typing speed
18915        } else {
18916          // Finished typing, wait before deleting
18917          typingTimeout = setTimeout(() => {
18918            isDeleting = true;
18919            typeWriter();
18920          }, 2000); // Pause before deleting
18921        }
18922      } else {
18923        // Deleting backward
18924        if (currentCharIndex > 0) {
18925          searchInput.placeholder = currentProduct.substring(0, currentCharIndex - 1);
18926          currentCharIndex--;
18927          typingTimeout = setTimeout(typeWriter, 50); // Deleting speed (faster)
18928        } else {
18929          // Finished deleting, move to next product
18930          isDeleting = false;
18931          currentProductIndex = (currentProductIndex + 1) % popularProducts.length;
18932          typingTimeout = setTimeout(typeWriter, 500); // Pause before typing next
18933        }
18934      }
18935    }
18936    
18937    function startTypingAnimation() {
18938      if (!isAIModeEnabled && !typingTimeout) {
18939        currentCharIndex = 0;
18940        isDeleting = false;
18941        typeWriter();
18942      }
18943    }
18944    
18945    function stopTypingAnimation() {
18946      if (typingTimeout) {
18947        clearTimeout(typingTimeout);
18948        typingTimeout = null;
18949      }
18950    }
18951    
18952    // Set initial placeholder
18953    updatePlaceholder();
18954    
18955    // Start typing animation for normal mode
18956    if (!isAIModeEnabled) {
18957      startTypingAnimation();
18958    }
18959
18960    function esc(str){
18961      if (str === null || str === undefined) return '';
18962      return String(str)
18963        .replace(/&/g,'&amp;')
18964        .replace(/</g,'&lt;')
18965        .replace(/>/g,'&gt;')
18966        .replace(/"/g,'&quot;')
18967        .replace(/'/g,'&#39;');
18968    }
18969
18970    function clearSuggest(){
18971      suggest.innerHTML = '';
18972      suggest.hidden = true;
18973      suggest.classList.remove('open', 'trending-mode');
18974      currentIndex = -1;
18975    }
18976
18977    // ==================== TRENDING FUNCTIONS ====================
18978    
18979    async function fetchTrending() {
18980      if (trendingCache) return trendingCache;
18981      if (trendingLoading) return null;
18982      
18983      trendingLoading = true;
18984      
18985      try {
18986        const res = await fetch('/api/trending_searches.php?timeframe=7d&limit=8');
18987        const data = await res.json();
18988        
18989        if (data.ok && Array.isArray(data.data)) {
18990          trendingCache = data.data;
18991          console.log('[Trending] Loaded', trendingCache.length, 'items');
18992          return trendingCache;
18993        }
18994      } catch (err) {
18995        console.error('[Trending] Fetch error:', err);
18996      } finally {
18997        trendingLoading = false;
18998      }
18999      
19000      return null;
19001    }
19002    
19003    async function fetchTrendingProducts() {
19004      if (trendingProductsCache) return trendingProductsCache;
19005      if (trendingProductsLoading) return null;
19006      
19007      trendingProductsLoading = true;
19008      
19009      try {
19010        const res = await fetch('/api/trending_products.php?timeframe=7d&limit=6');
19011        const data = await res.json();
19012        
19013        if (data.ok && Array.isArray(data.data)) {
19014          trendingProductsCache = data.data;
19015          console.log('[Trending Products] Loaded', trendingProductsCache.length, 'items');
19016          return trendingProductsCache;
19017        }
19018      } catch (err) {
19019        console.error('[Trending Products] Fetch error:', err);
19020      } finally {
19021        trendingProductsLoading = false;
19022      }
19023      
19024      return null;
19025    }
19026    
19027    function renderTrendingSuggestions(trends, products) {
19028      if ((!trends || trends.length === 0) && (!products || products.length === 0)) {
19029        clearSuggest();
19030        return;
19031      }
19032      
19033      let html = `<div class="trending-container">`;
19034      
19035      // Left column: Trending searches
19036      if (trends && trends.length > 0) {
19037        html += `
19038          <div class="trending-column trending-searches">
19039            <div class="trending-header">
19040              <span class="trending-icon">🔥</span>
19041              <span class="trending-title">Pencarian Trending</span>
19042            </div>
19043        `;
19044        
19045        html += trends.map((t, idx) => {
19046          const categoryHint = t.category_hint ? `<span class="trending-category">${esc(t.category_hint)}</span>` : '';
19047          return `
19048            <div class="trending-item" data-query="${esc(t.query)}" data-index="${idx}">
19049              <span class="trending-rank">
19050                <img src="/assets//img/uptrend.image" alt="trending" width="16" height="16" style="vertical-align: middle;">
19051              </span>
19052              <span class="trending-query">${esc(t.query)}</span>
19053              ${categoryHint}
19054            </div>
19055          `;
19056        }).join('');
19057        
19058        html += `</div>`;
19059      }
19060      
19061      // Right column: Trending products
19062      if (products && products.length > 0) {
19063        html += `
19064          <div class="trending-column trending-products">
19065            <div class="trending-header">
19066              <span class="trending-icon">⭐</span>
19067              <span class="trending-title">Produk Populer</span>
19068            </div>
19069        `;
19070        
19071        html += products.map((p, idx) => {
19072          return `
19073            <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}">
19074              <img src="${esc(p.image)}" alt="${esc(p.name)}" class="trending-product-thumb">
19075              <div class="trending-product-info">
19076                <div class="trending-product-name">${esc(p.name)}</div>
19077                <div class="trending-product-price">${esc(p.price_label)}</div>
19078              </div>
19079            </div>
19080          `;
19081        }).join('');
19082        
19083        html += `</div>`;
19084      }
19085      
19086      html += `</div>`;
19087      
19088      suggest.innerHTML = html;
19089      suggest.hidden = false;
19090      suggest.classList.add('open', 'trending-mode');
19091      suggest.style.display = 'block';
19092      
19093      console.log('[Trending] Rendered', trends?.length || 0, 'searches and', products?.length || 0, 'products');
19094    }
19095    
19096    function trackSearchClick(query, productId) {
19097      fetch('/api/track_search_click.php', {
19098        method: 'POST',
19099        headers: { 'Content-Type': 'application/json' },
19100        body: JSON.stringify({
19101          query: query,
19102          product_id: productId,
19103          click_type: 'suggestion'
19104        })
19105      }).catch(() => {}); // silent fail
19106    }
19107    
19108    function trackTrendingClick(query) {
19109      fetch('/api/track_search_click.php', {
19110        method: 'POST',
19111        headers: { 'Content-Type': 'application/json' },
19112        body: JSON.stringify({
19113          query: query,
19114          click_type: 'trending'
19115        })
19116      }).catch(() => {});
19117    }
19118    
19119    function trackTrendingProductClick(productId, query) {
19120      fetch('/api/track_search_click.php', {
19121        method: 'POST',
19122        headers: { 'Content-Type': 'application/json' },
19123        body: JSON.stringify({
19124          query: query || 'trending_product',
19125          product_id: productId,
19126          click_type: 'trending_product'
19127        })
19128      }).catch(() => {});
19129    }
19130    
19131    function trackSearchSubmit(query) {
19132      fetch('/api/track_search_click.php', {
19133        method: 'POST',
19134        headers: { 'Content-Type': 'application/json' },
19135        body: JSON.stringify({
19136          query: query,
19137          click_type: 'submit'
19138        })
19139      }).catch(() => {});
19140    }
19141    
19142    function logFinalSearch(query) {
19143      // Log the final search to analytics (not during typing)
19144      fetch('/api/search_products.php?q=' + encodeURIComponent(query) + '&log=1', {
19145        headers: { 'Accept': 'application/json' }
19146      }).catch(() => {});
19147    }
19148
19149    // ==================== END TRENDING FUNCTIONS ====================
19150
19151    function clearSuggest(){
19152      suggest.innerHTML = '';
19153      suggest.hidden = true;
19154      suggest.classList.remove('open', 'trending-mode');
19155      currentIndex = -1;
19156    }
19157
19158    // AI Toggle Handler
19159    aiToggleBtn.addEventListener('click', (e) => {
19160      e.preventDefault();
19161      e.stopPropagation();
19162      
19163      isAIModeEnabled = !isAIModeEnabled;
19164      
19165      if (isAIModeEnabled) {
19166        aiToggleBtn.classList.add('active');
19167        localStorage.setItem('plazait_ai_search_mode', 'enabled');
19168        updatePlaceholder(); // Update placeholder text
19169        stopTypingAnimation(); // Stop typing animation in AI mode
19170        
19171        // Show AI prompt if there's a query
19172        const query = searchInput.value.trim();
19173        if (query.length >= MIN_LEN) {
19174          showAISearchPrompt(query);
19175        } else {
19176          showAIActivationMessage();
19177        }
19178      } else {
19179        aiToggleBtn.classList.remove('active');
19180        localStorage.setItem('plazait_ai_search_mode', 'disabled');
19181        updatePlaceholder(); // Update placeholder text
19182        startTypingAnimation(); // Start typing animation in normal mode
19183        clearSuggest();
19184        
19185        // Switch to normal search if there's a query
19186        const query = searchInput.value.trim();
19187        if (query.length >= MIN_LEN) {
19188          performSearch(query);
19189        }
19190      }
19191    });
19192
19193    function showAIActivationMessage() {
19194      suggest.innerHTML = `
19195        <div class="ai-progress-container">
19196          <div class="ai-progress-header">
19197            <div class="ai-progress-icon">✨</div>
19198            <div class="ai-progress-text">
19199              <div class="ai-progress-title">AI Search Mode Activated!</div>
19200              <div class="ai-progress-subtitle">Ketik query Anda untuk pencarian cerdas</div>
19201            </div>
19202          </div>
19203        </div>
19204      `;
19205      suggest.hidden = false;
19206      suggest.classList.add('open');
19207      suggest.style.display = 'block';
19208    }
19209
19210    function renderAIInsights(insights){
19211      if (!insights) return '';
19212      
19213      // Generate AI summary via backend endpoint
19214      const summary = insights.ai_summary || generateAISummary(insights);
19215      
19216      if (!summary) return '';
19217      
19218      return `
19219        <div class="ai-result-banner">
19220          <div class="ai-result-icon">✨</div>
19221          <div class="ai-result-text">
19222            <div class="ai-result-title">AI Assistant</div>
19223            <div class="ai-result-description">${summary}</div>
19224          </div>
19225        </div>
19226      `;
19227    }
19228    
19229    function generateAISummary(insights) {
19230      // Fallback client-side summary generation if backend doesn't provide ai_summary
19231      const productName = insights.product_name || '';
19232      const categoryPath = insights.category_path || '';
19233      const priceRange = insights.price_range || {};
19234      const intent = insights.intent || '';
19235      
19236      let summary = '';
19237      
19238      // Parse category for natural display
19239      let categoryDisplay = '';
19240      if (categoryPath) {
19241        const categoryParts = categoryPath.split('/');
19242        categoryDisplay = categoryParts[categoryParts.length - 1] || categoryParts[categoryParts.length - 2] || categoryPath;
19243      }
19244      
19245      // Intent-based opening
19246      if (intent === 'cek_stok') {
19247        summary = `Berikut ketersediaan stok untuk <strong>${esc(productName)}</strong>. `;
19248      } else if (intent === 'rekomendasi_produk') {
19249        summary = `Berikut rekomendasi produk <strong>${esc(categoryDisplay || productName)}</strong> yang kami pilihkan untuk Anda. `;
19250      } else {
19251        // Default: cari_harga or general
19252        if (productName && categoryDisplay) {
19253          summary = `Kami menemukan <strong>${esc(productName)}</strong> di kategori ${esc(categoryDisplay)}`;
19254        } else if (productName) {
19255          summary = `Kami menemukan produk <strong>${esc(productName)}</strong>`;
19256        } else if (categoryDisplay) {
19257          summary = `Kami menemukan produk ${esc(categoryDisplay)}`;
19258        }
19259        
19260        // Add price context
19261        if (priceRange.min > 0 && priceRange.max > 0) {
19262          const minPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.min);
19263          const maxPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.max);
19264          summary += ` dengan kisaran harga ${minPrice} - ${maxPrice}`;
19265        } else if (priceRange.min > 0) {
19266          const minPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.min);
19267          summary += ` dengan harga mulai ${minPrice}`;
19268        } else if (priceRange.max > 0) {
19269          const maxPrice = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(priceRange.max);
19270          summary += ` dengan harga maksimal ${maxPrice}`;
19271        }
19272        
19273        summary += ' yang sesuai dengan kebutuhan Anda.';
19274      }
19275      
19276      return summary || esc(insights.explanation || '');
19277    }
19278
19279    function showAIProgressSteps() {
19280      return `
19281        <div class="ai-progress-container">
19282          <div class="ai-progress-header">
19283            <div class="ai-progress-icon">🤖</div>
19284            <div class="ai-progress-text">
19285              <div class="ai-progress-title">AI sedang menganalisis...</div>
19286              <div class="ai-progress-subtitle">Memproses query Anda dengan kecerdasan buatan</div>
19287            </div>
19288          </div>
19289          <div class="ai-progress-steps">
19290            <div class="ai-progress-step active">
19291              <div class="ai-step-icon">1</div>
19292              <div class="ai-step-text">Memahami maksud pencarian...</div>
19293            </div>
19294            <div class="ai-progress-step pending">
19295              <div class="ai-step-icon">2</div>
19296              <div class="ai-step-text">Mengekstrak parameter pencarian...</div>
19297            </div>
19298            <div class="ai-progress-step pending">
19299              <div class="ai-step-icon">3</div>
19300              <div class="ai-step-text">Mencari produk yang relevan...</div>
19301            </div>
19302          </div>
19303        </div>
19304      `;
19305    }
19306
19307    function updateAIProgressStep(step) {
19308      const steps = suggest.querySelectorAll('.ai-progress-step');
19309      
19310      steps.forEach((stepEl, idx) => {
19311        const icon = stepEl.querySelector('.ai-step-icon');
19312        
19313        if (idx < step) {
19314          stepEl.classList.remove('active', 'pending');
19315          stepEl.classList.add('completed');
19316          icon.textContent = '✓';
19317        } else if (idx === step) {
19318          stepEl.classList.remove('pending', 'completed');
19319          stepEl.classList.add('active');
19320        }
19321      });
19322    }
19323
19324    function renderSuggest(items, aiInsights){
19325      if (!items.length){
19326        clearSuggest();
19327        return;
19328      }
19329      
19330      let html = '';
19331      
19332      // Show AI insights if available
19333      if (aiInsights && aiInsights.explanation) {
19334        html += renderAIInsights(aiInsights);
19335      }
19336      
19337      // Section header for products with hint
19338      html += `
19339        <div class="suggest-section-header">
19340          <div class="suggest-header-main">
19341            <span class="suggest-header-icon">📦</span>
19342            <span class="suggest-header-text">Produk yang ditemukan (${items.length})</span>
19343          </div>
19344          <div class="suggest-header-hint">Tekan <kbd>Enter</kbd> untuk lihat semua hasil</div>
19345        </div>
19346      `;
19347      
19348      // Render product items
19349      html += items.map((it, idx)=>{
19350        // Normalisasi & fallback
19351        const id    = it.id != null ? it.id : '';
19352        const name  = esc(it.name);
19353        const slug  = esc(it.slug != null ? it.slug : it.id);
19354        const img   = esc(it.image || '/assets//img/no-image.png');
19355        const price = esc(it.price_label || '');
19356
19357       const url = '/product/' + encodeURIComponent(slug);
19358
19359        return `
19360          <div class="suggest-item" data-index="${idx}" data-url="${url}" data-id="${esc(id)}">
19361            <img src="${img}" alt="${name}" class="suggest-thumb">
19362            <div class="suggest-meta">
19363              <div class="suggest-name">${name}</div>
19364              <div class="suggest-price">${price}</div>
19365            </div>
19366          </div>
19367        `;
19368      }).join('');
19369      
19370      // Footer hint for more results - only show if 10 items (means there might be more)
19371      if (items.length >= 10) {
19372        html += `
19373          <div class="suggest-footer-hint">
19374            <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="vertical-align: -3px; margin-right: 6px;">
19375              <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"/>
19376            </svg>
19377            <span>Lihat hasil lengkap dengan menekan <kbd>Enter</kbd> atau <a href="#" class="suggest-footer-link" id="footerSearchLink">klik di sini</a></span>
19378          </div>
19379        `;
19380      }
19381      
19382      suggest.innerHTML = html;
19383      suggest.hidden = false;
19384      suggest.classList.add('open');
19385      suggest.style.display = 'block';
19386      suggest.style.visibility = 'visible';
19387      suggest.style.opacity = '1';
19388    }
19389
19390    function fetchRemote(q, shouldLog = false){
19391      // Cancel previous request if any
19392      if (currentAbortController) {
19393        currentAbortController.abort();
19394      }
19395      currentAbortController = new AbortController();
19396
19397      // Always use standard search for normal mode
19398      // Only add log=1 parameter when shouldLog is true (intentional search)
19399      const logParam = shouldLog ? '&log=1' : '';
19400      const url = '/api/search_products.php?q=' + encodeURIComponent(q) + logParam;
19401      const fetchOptions = {
19402        headers: {'Accept':'application/json'},
19403        signal: currentAbortController.signal
19404      };
19405
19406      return fetch(url, fetchOptions)
19407        .then(r=>r.json())
19408        .then(j=>{
19409          if (j.ok && Array.isArray(j.data)) {
19410            return j;
19411          }
19412          return { ok: false, data: [] };
19413        })
19414        .catch(err=>{
19415          if (err.name === 'AbortError') {
19416            return { ok: false, data: [] };
19417          }
19418          return { ok:false, data:[] };
19419        });
19420    }
19421
19422    function showNoResults(q, aiInsights) {
19423      suggest.innerHTML = `
19424        <div style="padding: 32px 24px; text-align: center; color: #64748b;">
19425          <div style="font-size: 56px; margin-bottom: 16px; opacity: 0.6;">🔍</div>
19426          <div style="font-size: 16px; font-weight: 600; color: #334155; margin-bottom: 8px;">
19427            Tidak ada hasil untuk "${esc(q)}"
19428          </div>
19429          ${aiInsights && aiInsights.explanation ? `
19430            <div style="font-size: 13px; margin: 16px auto; padding: 12px 16px; background: #f1f5f9; border-radius: 8px; max-width: 400px; color: #475569;">
19431              <strong style="color: #EAB308;">🤖 AI memahami:</strong><br>
19432              ${esc(aiInsights.explanation)}
19433            </div>
19434          ` : ''}
19435          <div style="font-size: 13px; margin-top: 16px; opacity: 0.8;">
19436            Coba kata kunci lain atau hubungi kami untuk bantuan
19437          </div>
19438        </div>
19439      `;
19440      suggest.hidden = false;
19441      suggest.classList.add('open');
19442      suggest.style.display = 'block';
19443    }
19444
19445    function showErrorMessage() {
19446      suggest.innerHTML = `
19447        <div style="padding: 32px 24px; text-align: center; color: #dc2626;">
19448          <div style="font-size: 48px; margin-bottom: 12px;">⚠️</div>
19449          <div style="font-size: 14px; font-weight: 600; margin-bottom: 8px;">
19450            Pencarian tidak tersedia
19451          </div>
19452          <div style="font-size: 12px; opacity: 0.8;">
19453            Silakan coba lagi dalam beberapa saat
19454          </div>
19455        </div>
19456      `;
19457      suggest.hidden = false;
19458      suggest.classList.add('open');
19459      suggest.style.display = 'block';
19460    }
19461
19462    function showAIErrorState(q) {
19463      suggest.innerHTML = `
19464        <div class="ai-progress-container">
19465          <div class="ai-progress-header">
19466            <div class="ai-progress-icon" style="background: linear-gradient(135deg, #CA8A04 0%, #CA8A04 100%);">⚡</div>
19467            <div class="ai-progress-text">
19468              <div class="ai-progress-title">AI sedang memproses...</div>
19469              <div class="ai-progress-subtitle">Menganalisis query: "${esc(q)}"</div>
19470            </div>
19471          </div>
19472          <div style="padding: 16px; margin-top: 12px; background: #FEF3C7; border-radius: 8px; border-left: 3px solid #CA8A04;">
19473            <div style="font-size: 13px; color: #8B6914; line-height: 1.6;">
19474              <strong style="display: block; margin-bottom: 4px;">💡 Sedang mencari produk terbaik untuk Anda</strong>
19475              AI kami sedang menganalisis database untuk menemukan hasil yang paling relevan.
19476              Mohon tunggu sebentar...
19477            </div>
19478          </div>
19479          <div style="padding: 12px 16px; margin-top: 12px; text-align: center;">
19480            <button onclick="location.reload()" style="padding: 8px 16px; background: #FBBF24; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600;">
19481              🔄 Refresh & Coba Lagi
19482            </button>
19483          </div>
19484        </div>
19485      `;
19486      suggest.hidden = false;
19487      suggest.classList.add('open');
19488      suggest.style.display = 'block';
19489    }
19490
19491    function triggerAISearch(q) {
19492      lastQuery = q; // Update lastQuery to prevent re-triggering
19493      isAIProcessing = true; // Set flag to prevent suggest box from closing
19494      
19495      // Show AI progress UI - keep suggest box visible
19496      suggest.innerHTML = showAIProgressSteps();
19497      suggest.hidden = false;
19498      suggest.classList.add('open');
19499      suggest.style.display = 'block';
19500      
19501      setTimeout(() => updateAIProgressStep(1), 1000);
19502      setTimeout(() => updateAIProgressStep(2), 2500);
19503
19504      // Cancel previous request if any
19505      if (currentAbortController) {
19506        currentAbortController.abort();
19507      }
19508      currentAbortController = new AbortController();
19509
19510      const url = '/api/search_ai.php?q=' + encodeURIComponent(q);
19511      const fetchOptions = {
19512        headers: {'Accept':'application/json'},
19513        signal: currentAbortController.signal
19514      };
19515      
19516      // Add timeout
19517      const timeoutId = setTimeout(() => {
19518        currentAbortController.abort();
19519        
19520        // Show fallback loading state
19521        suggest.innerHTML = `
19522          <div class="ai-progress-container">
19523            <div class="ai-progress-header">
19524              <div class="ai-progress-icon">⏳</div>
19525              <div class="ai-progress-text">
19526                <div class="ai-progress-title">AI timeout - switching to standard search</div>
19527                <div class="ai-progress-subtitle">Mencari dengan metode tradisional...</div>
19528              </div>
19529            </div>
19530          </div>
19531        `;
19532        
19533        // Fallback to traditional search
19534        fetch('/api/search_products.php?q=' + encodeURIComponent(q), {
19535          headers: { 'Accept': 'application/json' }
19536        })
19537          .then(r => r.json())
19538          .then(j => {
19539            isAIProcessing = false; // Reset flag
19540            if (j.ok && Array.isArray(j.data) && j.data.length) {
19541              renderSuggest(j.data, null);
19542            } else {
19543              showNoResults(q, null);
19544            }
19545          })
19546          .catch(() => {
19547            isAIProcessing = false; // Reset flag
19548            showAIErrorState(q);
19549          });
19550      }, AI_TIMEOUT);
19551
19552      fetch(url, fetchOptions)
19553        .then(r => r.json())
19554        .then(j => {
19555          clearTimeout(timeoutId);
19556          isAIProcessing = false; // Reset flag when done
19557          
19558          if (j.ok && Array.isArray(j.data)) {
19559            if (j.data.length > 0) {
19560              renderSuggest(j.data, j.ai_insights || null);
19561            } else {
19562              showNoResults(q, j.ai_insights || null);
19563            }
19564          } else {
19565            showAIErrorState(q);
19566          }
19567        })
19568        .catch(err => {
19569          clearTimeout(timeoutId);
19570          isAIProcessing = false; // Reset flag on error
19571          if (err.name !== 'AbortError') {
19572            showAIErrorState(q);
19573          }
19574        });
19575    }
19576
19577    function performSearch(q){
19578      if (q.length < MIN_LEN){
19579        clearSuggest();
19580        return;
19581      }
19582      if (q === lastQuery) return;
19583      lastQuery = q;
19584      
19585      // Normal mode only - AI mode uses triggerAISearch()
19586      fetchRemote(q).then(j=>{
19587        if (j.ok && Array.isArray(j.data)){
19588          if (j.data.length > 0) {
19589            renderSuggest(j.data, null);
19590          } else {
19591            showNoResults(q, null);
19592          }
19593        } else {
19594          clearSuggest();
19595        }
19596      }).catch(() => {
19597        clearSuggest();
19598      });
19599    }
19600
19601    function debounce(){
19602      const q = searchInput.value.trim();
19603      clearTimeout(debounceTimer);
19604      
19605      // If empty, show trending suggestions and products
19606      if (q.length === 0) {
19607        debounceTimer = setTimeout(async () => {
19608          const trends = trendingCache || await fetchTrending();
19609          const products = trendingProductsCache || await fetchTrendingProducts();
19610          if ((trends && trends.length > 0) || (products && products.length > 0)) {
19611            renderTrendingSuggestions(trends, products);
19612          } else {
19613            clearSuggest();
19614          }
19615        }, 100);
19616        return;
19617      }
19618      
19619      // For AI mode, just show search prompt (no auto search)
19620      if (isAIModeEnabled && q.length >= MIN_LEN) {
19621        showAISearchPrompt(q);
19622      } else if (!isAIModeEnabled && q.length >= MIN_LEN) {
19623        // Normal mode: auto search with debounce
19624        debounceTimer = setTimeout(()=> performSearch(q), DEBOUNCE);
19625      } else {
19626        clearSuggest();
19627      }
19628    }
19629
19630    function showAISearchPrompt(q) {
19631      suggest.innerHTML = `
19632        <div class="ai-search-prompt">
19633          <div class="ai-prompt-header">
19634            <div class="ai-prompt-icon">🤖</div>
19635            <div class="ai-prompt-text">
19636              <div class="ai-prompt-title">Pencarian AI Siap!</div>
19637              <div class="ai-prompt-subtitle">Klik tombol di bawah untuk mencari dengan AI</div>
19638            </div>
19639          </div>
19640          <div class="ai-prompt-query">
19641            <div class="ai-query-label">
19641Query Anda:</div>
19642            <div class="ai-query-text">"${esc(q)}"</div>
19643          </div>
19644          <button class="ai-search-trigger-btn" data-query="${esc(q)}">
19645            <span class="ai-btn-icon">✨</span>
19646            <span class="ai-btn-text">Cari dengan AI</span>
19647          </button>
19648          <div class="ai-prompt-footer">
19649            AI akan menganalisis query Anda untuk hasil yang lebih relevan
19650          </div>
19651        </div>
19652      `;
19653      suggest.hidden = false;
19654      suggest.classList.add('open');
19655      suggest.style.display = 'block';
19656    }
19657
19658    function moveHighlight(dir){
19659      if (suggest.hidden) return;
19660      const items = suggest.querySelectorAll('.suggest-item');
19661      if (!items.length) return;
19662      currentIndex += dir;
19663      if (currentIndex < 0) currentIndex = items.length - 1;
19664      if (currentIndex >= items.length) currentIndex = 0;
19665      items.forEach(it=> it.classList.remove('active'));
19666      const active = items[currentIndex];
19667      if (active){
19668        active.classList.add('active');
19669        active.scrollIntoView({block:'nearest'});
19670      }
19671    }
19672
19673    function activate(){
19674      const active = suggest.querySelector('.suggest-item.active');
19675      if (!active) return;
19676      window.location.href = active.getAttribute('data-url');
19677    }
19678
19679    searchInput.addEventListener('input', debounce);
19680    
19681    // Stop typing animation when focused & load trending if empty
19682    searchInput.addEventListener('focus', async () => {
19683      stopTypingAnimation();
19684      
19685      const q = searchInput.value.trim();
19686      
19687      // If empty, show trending suggestions and products
19688      if (q.length === 0) {
19689        const trends = trendingCache || await fetchTrending();
19690        const products = trendingProductsCache || await fetchTrendingProducts();
19691        if ((trends && trends.length > 0) || (products && products.length > 0)) {
19692          renderTrendingSuggestions(trends, products);
19693        }
19694        return;
19695      }
19696      
19697      // If AI mode and has query, show AI prompt
19698      if (isAIModeEnabled && q.length >= MIN_LEN && suggest.hidden) {
19699        showAISearchPrompt(q);
19700      }
19701    });
19702    
19703    // Resume typing animation when blurred (if suggest is closed)
19704    searchInput.addEventListener('blur', () => {
19705      // Add delay to allow click events to fire first
19706      setTimeout(() => {
19707        if (!isAIModeEnabled && suggest.hidden && searchInput.value.trim() === '') {
19708          startTypingAnimation();
19709        }
19710      }, 200);
19711    });
19712    
19713    searchInput.addEventListener('keydown', e=>{
19714      switch(e.key){
19715        case 'ArrowDown': moveHighlight(1); e.preventDefault(); break;
19716        case 'ArrowUp': moveHighlight(-1); e.preventDefault(); break;
19717        case 'Enter':
19718          if (!suggest.hidden){
19719            activate();
19720            e.preventDefault();
19721          } else {
19722            // Direct enter without suggestion - track search submit and log final search
19723            const q = searchInput.value.trim();
19724            if (q.length >= MIN_LEN) {
19725              trackSearchSubmit(q);
19726              logFinalSearch(q);
19727            }
19728          }
19729          break;
19730        case 'Escape': clearSuggest(); break;
19731      }
19732    });
19733
19734    suggest.addEventListener('click', e=>{
19735      // Check if footer link clicked
19736      const footerLink = e.target.closest('#footerSearchLink');
19737      if (footerLink) {
19738        e.preventDefault();
19739        const q = searchInput.value.trim();
19740        if (q.length >= MIN_LEN) {
19741          trackSearchSubmit(q);
19742          logFinalSearch(q);
19743          window.location.href = '/product/search/?q=' + encodeURIComponent(q);
19744        }
19745        return;
19746      }
19747      
19748      // Check if AI search button clicked
19749      const aiBtn = e.target.closest('.ai-search-trigger-btn');
19750      if (aiBtn) {
19751        const query = aiBtn.getAttribute('data-query');
19752        if (query) {
19753          triggerAISearch(query);
19754        }
19755        return;
19756      }
19757      
19758      // Handle trending item click
19759      const trendingItem = e.target.closest('.trending-item');
19760      if (trendingItem) {
19761        const query = trendingItem.getAttribute('data-query');
19762        if (query) {
19763          searchInput.value = query;
19764          trackTrendingClick(query);
19765          logFinalSearch(query); // Log final search when user clicks trending
19766          performSearch(query);
19767        }
19768        return;
19769      }
19770      
19771      // Handle trending product click
19772      const trendingProduct = e.target.closest('.trending-product-item');
19773      if (trendingProduct) {
19774        const productId = trendingProduct.getAttribute('data-product-id');
19775        const slug = trendingProduct.getAttribute('data-slug');
19776        const query = trendingProduct.getAttribute('data-query');
19777        
19778        if (productId && slug) {
19779          trackTrendingProductClick(productId, query);
19780          window.location.href = '/product/' + encodeURIComponent(slug);
19781        }
19782        return;
19783      }
19784      
19785      // Handle product item click
19786      const it = e.target.closest('.suggest-item');
19787      if (!it) return;
19788      
19789      const productId = it.getAttribute('data-id');
19790      trackSearchClick(lastQuery, productId);
19791      logFinalSearch(lastQuery); // Log final search when user clicks on suggestion
19792      
19793      window.location.href = it.getAttribute('data-url');
19794    });
19795
19796    document.addEventListener('click', e=>{
19797      // Don't close suggest box if AI is processing
19798      if (isAIProcessing) return;
19799      
19800      if (e.target === searchInput || e.target === aiToggleBtn || 
19801          suggest.contains(e.target) || aiToggleBtn.contains(e.target)) return;
19802      clearSuggest();
19803    });
19804
19805    if (searchBtn){
19806      searchBtn.addEventListener('click', ()=>{
19807        const q = searchInput.value.trim();
19808        if (q.length >= MIN_LEN){
19809          // Track search submit and log final search
19810          trackSearchSubmit(q);
19811          logFinalSearch(q);
19812          window.location.href = '/product/search/?q=' + encodeURIComponent(q);
19813        }
19814      });
19815    }
19816
19817    // Pastikan style dasar muncul
19818    if (!suggest.classList.contains('wired')) {
19819      suggest.classList.add('wired');
19820      suggest.style.position = 'absolute';
19821      suggest.style.zIndex = '9999';
19822      suggest.style.background = '#fff';
19823      suggest.style.border = '1px solid #ccc';
19824      suggest.style.boxShadow = '0 4px 12px rgba(0,0,0,0.12)';
19825      suggest.style.maxHeight = '360px';
19826      suggest.style.overflowY = 'auto';
19827    }
19828
19829    return true;
19830  }
19831
19832  if (!setup()) {
19833    if (document.readyState === 'loading') {
19834      document.addEventListener('DOMContentLoaded', setup);
19835    } else {
19836      setTimeout(setup, 80);
19837    }
19838  }
19839})();
19840</script>
vendor: 2 bytes, line 19840
19840
19841<script data-inlined="site-notice.js">
19842/**
19843 * Site Notice Banner - Interactive Behavior
19844 * Handles dismiss functionality with localStorage persistence
19845 * Supports dynamic notice IDs for different campaigns
19846 */
19847
19848(function() {
19849    'use strict';
19850
19851    const STORAGE_KEY = 'plazait_site_notice_dismissed';
19852    
19853    const banner = document.getElementById('holidayNotice');
19854    const closeBtn = document.getElementById('holidayNoticeClose');
19855    
19856    if (!banner || !closeBtn) return;
19857
19858    // Get notice ID from data attribute
19859    const noticeId = banner.dataset.noticeId;
19860    if (!noticeId) {
19861        console.warn('Notice ID not found. Banner will not persist dismiss state.');
19862    }
19863
19864    /**
19865     * Check if banner was previously dismissed
19866     */
19867    function wasDismissed() {
19868        if (!noticeId) return false;
19869        
19870        try {
19871            const dismissed = localStorage.getItem(STORAGE_KEY);
19872            return dismissed === noticeId;
19873        } catch (e) {
19874            console.warn('localStorage not available:', e);
19875            return false;
19876        }
19877    }
19878
19879    /**
19880     * Mark banner as dismissed
19881     */
19882    function markDismissed() {
19883        if (!noticeId) return;
19884        
19885        try {
19886            localStorage.setItem(STORAGE_KEY, noticeId);
19887        } catch (e) {
19888            console.warn('Could not save to localStorage:', e);
19889        }
19890    }
19891
19892    /**
19893     * Hide banner with animation
19894     */
19895    function hideBanner() {
19896        banner.classList.add('hidden');
19897        document.body.classList.remove('has-holiday-notice');
19898        
19899        // Remove from DOM after animation
19900        setTimeout(() => {
19901            banner.remove();
19902        }, 300);
19903        
19904        markDismissed();
19905        
19906        // Analytics tracking (optional)
19907        if (window.gtag) {
19908            window.gtag('event', 'banner_dismiss', {
19909                'event_category': 'Site Notice',
19910                'event_label': noticeId || 'unknown',
19911            });
19912        }
19913    }
19914
19915    /**
19916     * Initialize banner
19917     */
19918    function init() {
19919        // Check if already dismissed
19920        if (wasDismissed()) {
19921            banner.remove();
19922            return;
19923        }
19924        
19925        // Add body class for layout adjustment
19926        document.body.classList.add('has-holiday-notice');
19927        
19928        // Close button click
19929        closeBtn.addEventListener('click', hideBanner);
19930        
19931        // Keyboard support for close button
19932        closeBtn.addEventListener('keydown', (e) => {
19933            if (e.key === 'Enter' || e.key === ' ') {
19934                e.preventDefault();
19935                hideBanner();
19936            }
19937        });
19938        
19939        // Optional: Auto-hide after certain time (disabled by default)
19940        // setTimeout(hideBanner, 30000); // 30 seconds
19941    }
19942
19943    // Initialize when DOM is ready
19944    if (document.readyState === 'loading') {
19945        document.addEventListener('DOMContentLoaded', init);
19946    } else {
19947        init();
19948    }
19949
19950})();
19951
19952</script>
vendor: 2 bytes, line 19952
19952
19953<script data-inlined="guest-cart.js">
19954/**
19955 * Guest Cart Manager - localStorage based cart for guest users
19956 * 
19957 * Manages shopping cart items in browser localStorage for users who are not logged in.
19958 * Provides seamless transition to database when user logs in.
19959 */
19960
19961class GuestCart {
19962    constructor() {
19963        this.storageKey = 'plazait_guest_cart';
19964        this.apiBase = '/api/cart';
19965        
19966        // Initialize cart structure
19967        this.cart = this.loadFromStorage();
19968        
19969        // Bind methods
19970        this.add = this.add.bind(this);
19971        this.remove = this.remove.bind(this);
19972        this.update = this.update.bind(this);
19973        this.clear = this.clear.bind(this);
19974        this.getItems = this.getItems.bind(this);
19975        this.mergeToDatabase = this.mergeToDatabase.bind(this);
19976    }
19977    
19978    /**
19979     * Load cart from localStorage
19980     */
19981    loadFromStorage() {
19982        try {
19983            const stored = localStorage.getItem(this.storageKey);
19984            if (stored) {
19985                const parsed = JSON.parse(stored);
19986                if (parsed && typeof parsed === 'object' && parsed.items) {
19987                    return parsed;
19988                }
19989            }
19990        } catch (error) {
19991            // Silent error handling
19992        }
19993        
19994        return {
19995            items: {},
19996            updated_at: Date.now(),
19997            version: '1.0'
19998        };
19999    }
20000    
20001    /**
20002     * Save cart to localStorage
20003     */
20004    saveToStorage() {
20005        try {
20006            this.cart.updated_at = Date.now();
20007            localStorage.setItem(this.storageKey, JSON.stringify(this.cart));
20008            
20009            // Dispatch custom event for cart updates
20010            window.dispatchEvent(new CustomEvent('guestCartUpdated', {
20011                detail: {
20012                    itemsCount: this.getItemsCount(),
20013                    linesCount: this.getLinesCount(),
20014                    items: this.cart.items
20015                }
20016            }));
20017            
20018            return true;
20019        } catch (error) {
20020            return false;
20021        }
20022    }
20023    
20024    /**
20025     * Generate cart item key
20026     */
20027    generateKey(productId, variantId = 0) {
20028        return `p${productId}v${variantId}`;
20029    }
20030    
20031    /**
20032     * Add item to guest cart
20033     */
20034    add(productId, qty = 1, variantId = 0, meta = {}) {
20035        const key = this.generateKey(productId, variantId);
20036        
20037        // Validate inputs
20038        if (!productId || productId <= 0 || !qty || qty <= 0) {
20039            throw new Error('Invalid product ID or quantity');
20040        }
20041        
20042        qty = Math.max(1, Math.min(999, parseInt(qty)));
20043        
20044        // Initialize item if not exists
20045        if (!this.cart.items[key]) {
20046            this.cart.items[key] = {
20047                pid: parseInt(productId),
20048                vid: parseInt(variantId) || 0,
20049                qty: 0,
20050                name: meta.name || '',
20051                sku: meta.sku || '',
20052                price: parseInt(meta.price) || 0,
20053                image_url: meta.image_url || '',
20054                product_url: meta.product_url || '/product/' + parseInt(productId),
20055                added_at: Date.now()
20056            };
20057        }
20058        
20059        // Update quantity
20060        this.cart.items[key].qty = Math.max(1, Math.min(999, this.cart.items[key].qty + qty));
20061        
20062        // Update metadata if provided
20063        if (meta.name) this.cart.items[key].name = meta.name;
20064        if (meta.sku) this.cart.items[key].sku = meta.sku;
20065        if (meta.price) this.cart.items[key].price = parseInt(meta.price);
20066        if (meta.image_url) this.cart.items[key].image_url = meta.image_url;
20067        if (meta.product_url) this.cart.items[key].product_url = meta.product_url;
20068        
20069        this.saveToStorage();
20070        
20071        return {
20072            key,
20073            item: this.cart.items[key],
20074            itemsCount: this.getItemsCount(),
20075            linesCount: this.getLinesCount()
20076        };
20077    }
20078    
20079    /**
20080     * Remove item from guest cart
20081     */
20082    remove(productId, variantId = 0) {
20083        const key = this.generateKey(productId, variantId);
20084        
20085        if (this.cart.items[key]) {
20086            delete this.cart.items[key];
20087            this.saveToStorage();
20088            
20089            return true;
20090        }
20091        
20092        return false;
20093    }
20094    
20095    /**
20096     * Update item quantity
20097     */
20098    update(productId, qty, variantId = 0) {
20099        const key = this.generateKey(productId, variantId);
20100        
20101        if (!this.cart.items[key]) {
20102            return false;
20103        }
20104        
20105        qty = parseInt(qty);
20106        if (qty <= 0) {
20107            return this.remove(productId, variantId);
20108        }
20109        
20110        this.cart.items[key].qty = Math.max(1, Math.min(999, qty));
20111        this.saveToStorage();
20112        
20113        return true;
20114    }
20115    
20116    /**
20117     * Clear entire cart
20118     */
20119    clear() {
20120        this.cart = {
20121            items: {},
20122            updated_at: Date.now(),
20123            version: '1.0'
20124        };
20125        
20126        this.saveToStorage();
20127        
20128        return true;
20129    }
20130    
20131    /**
20132     * Get all cart items
20133     */
20134    getItems() {
20135        return this.cart.items;
20136    }
20137    
20138    /**
20139     * Get cart items as array
20140     */
20141    getItemsArray() {
20142        return Object.values(this.cart.items);
20143    }
20144    
20145    /**
20146     * Get total items count (sum of quantities)
20147     */
20148    getItemsCount() {
20149        return Object.values(this.cart.items).reduce((total, item) => total + (item.qty || 0), 0);
20150    }
20151    
20152    /**
20153     * Get lines count (number of different items)
20154     */
20155    getLinesCount() {
20156        return Object.keys(this.cart.items).length;
20157    }
20158    
20159    /**
20160     * Get cart summary
20161     */
20162    getSummary() {
20163        const itemsCount = this.getItemsCount();
20164        const linesCount = this.getLinesCount();
20165        const totalValue = Object.values(this.cart.items).reduce(
20166            (total, item) => total + ((item.qty || 0) * (item.price || 0)), 0
20167        );
20168        
20169        return {
20170            itemsCount,
20171            linesCount,
20172            totalValue,
20173            updatedAt: this.cart.updated_at
20174        };
20175    }
20176    
20177    /**
20178     * Check if cart has items
20179     */
20180    isEmpty() {
20181        return this.getLinesCount() === 0;
20182    }
20183    
20184    /**
20185     * Merge guest cart to database after user login
20186     */
20187    async mergeToDatabase() {
20188        const items = this.cart.items;
20189        
20190        if (Object.keys(items).length === 0) {
20191            return { ok: true, merged_count: 0, message: 'No items to merge' };
20192        }
20193        
20194        try {
20195            // Use new endpoint that handles localStorage format
20196            const response = await fetch(`${this.apiBase}/merge-localStorage.php`, {
20197                method: 'POST',
20198                headers: {
20199                    'Content-Type': 'application/json',
20200                },
20201                credentials: 'same-origin',
20202                body: JSON.stringify({
20203                    items: items  // Send localStorage format directly
20204                })
20205            });
20206            
20207            const result = await response.json();
20208            
20209            if (result.ok) {
20210                // Clear localStorage cart after successful merge
20211                this.clear();
20212                
20213                // Update cart counter
20214                if (window.updateCartCounter) {
20215                    try {
20216                        await window.updateCartCounter();
20217                    } catch (e) {
20218                        // Silent error handling
20219                    }
20220                }
20221                
20222                // Dispatch merge completion event
20223                window.dispatchEvent(new CustomEvent('guestCartMerged', {
20224                    detail: result
20225                }));
20226                
20227                return result;
20228            } else {
20229                throw new Error(result.error || 'Failed to merge cart');
20230            }
20231            
20232        } catch (error) {
20233            throw error;
20234        }
20235    }
20236    
20237    /**
20238     * Export cart data (for debugging or backup)
20239     */
20240    export() {
20241        return {
20242            ...this.cart,
20243            summary: this.getSummary()
20244        };
20245    }
20246    
20247    /**
20248     * Import cart data (for restore or testing)
20249     */
20250    import(cartData) {
20251        if (cartData && typeof cartData === 'object' && cartData.items) {
20252            this.cart = {
20253                items: cartData.items,
20254                updated_at: cartData.updated_at || Date.now(),
20255                version: cartData.version || '1.0'
20256            };
20257            
20258            this.saveToStorage();
20259            return true;
20260        }
20261        
20262        return false;
20263    }
20264}
20265
20266// Create global instance
20267window.guestCart = new GuestCart();
20268
20269// Auto-merge on page load if user is logged in
20270document.addEventListener('DOMContentLoaded', async function() {
20271    // Multiple ways to check if user is logged in
20272    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
20273                      document.body.classList.contains('logged-in') ||
20274                      window.userLoggedIn === true ||
20275                      document.body.dataset.userLoggedIn === 'true';
20276    
20277    if (isLoggedIn && !window.guestCart.isEmpty()) {
20278        // Small delay to ensure page is fully loaded
20279        setTimeout(async () => {
20280            try {
20281                const result = await window.guestCart.mergeToDatabase();
20282            } catch (error) {
20283                // Silent error handling
20284            }
20285        }, 500);
20286    }
20287});
20288
20289// Also listen for login events (if login happens via AJAX)
20290window.addEventListener('userLoggedIn', async function() {
20291    if (!window.guestCart.isEmpty()) {
20292        try {
20293            await window.guestCart.mergeToDatabase();
20294        } catch (error) {
20295            // Silent error handling
20296        }
20297    }
20298});
20299</script>
vendor: 2 bytes, line 20299
20299
20300<script data-inlined="cart.js">
20301(function(){
20302  function $(s,ctx=document){ return ctx.querySelector(s); }
20303  function esc(s){ return String(s||'').replace(/[&<>"']/g, m=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;' }[m])); }
20304  
20305  // CSRF token helper
20306  function getCsrfToken() {
20307    const metaTag = document.querySelector('meta[name="csrf-token"]');
20308    return metaTag ? metaTag.getAttribute('content') : '';
20309  }
20310  
20311  function guessPdMeta(){
20312    const name = (window.PD_NAME !== undefined) ? String(window.PD_NAME) : ($('#pd-title')?.textContent || '');
20313    const sku  = (function(){ const el=$('#pd-sku'); return el ? el.textContent.trim() : (window.PD_SKU||''); })();
20314    // SECURITY FIX: Remove price from client-side metadata (will be validated server-side)
20315    const imgEl = $('#pd-main-img');
20316    const image_url = imgEl ? (imgEl.getAttribute('src') || '') : '';
20317    return { name, sku, image_url }; // price removed for security
20318  }
20319
20320  async function addToCart(productId, qty=1, variantId=0, metaOpt){
20321    const meta = Object.assign(guessPdMeta(), metaOpt||{});
20322    const payload = {
20323      product_id: parseInt(productId||0,10) || 0,
20324      qty: parseInt(qty||1,10) || 1,
20325      variant_id: parseInt(variantId||0,10) || 0,
20326      meta
20327    };
20328    
20329    // Check if user is logged in
20330    const isLoggedIn = document.querySelector('meta[name="user-logged-in"]')?.content === 'true' ||
20331                      document.body.classList.contains('logged-in') ||
20332                      window.userLoggedIn === true ||
20333                      document.body.dataset.userLoggedIn === 'true';
20334    
20335    // Skip localStorage untuk logged user - mereka pakai database
20336    if (!isLoggedIn && window.guestCart) {
20337      try {
20338        window.guestCart.add(payload.product_id, payload.qty, payload.variant_id, meta);
20339      } catch (e) {
20340        console.warn('Failed to add to localStorage cart:', e);
20341      }
20342    }
20343    
20344    const res = await fetch('/api/cart/add.php', {
20345      method: 'POST',
20346      headers: { 
20347        'Content-Type':'application/json',
20348        'X-CSRF-Token': getCsrfToken()
20349      },
20350      credentials: 'same-origin',
20351      body: JSON.stringify({
20352        ...payload,
20353        csrf_token: getCsrfToken()
20354      }),
20355    });
20356    const data = await res.json().catch(()=>({ ok:false, error:'Gagal menambahkan ke keranjang' }));
20357    if (!res.ok || !data.ok) {
20358      const msg = data && data.error ? data.error : 'Gagal menambahkan ke keranjang';
20359      throw new Error(msg);
20360    }
20361
20362    // Update badge di navbar dengan data dari response
20363    try {
20364      const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
20365      if (el && data.cart && typeof data.cart.items_count === 'number') {
20366        el.textContent = String(data.cart.items_count);
20367        el.classList.toggle('is-empty', data.cart.items_count <= 0);
20368      }
20369    } catch(_){}
20370
20371    // Siarkan event global (dipakai cart-badge.js dan halaman lain)
20372    try {
20373      document.dispatchEvent(new CustomEvent('cart:updated', { detail: data.cart || {} }));
20374    } catch(_){}
20375
20376    // Update CartBadge dengan data dari response (lebih reliable)
20377    try { 
20378      if (window.CartBadge && window.CartBadge.set) {
20379        window.CartBadge.set(data.cart.items_count||0);
20380      }
20381    } catch(_){}
20382
20383    // Tambahan: refresh cart counter setelah delay kecil untuk memastikan data tersinkronisasi
20384    setTimeout(() => {
20385      if (window.updateCartCounter) {
20386        window.updateCartCounter();
20387      }
20388    }, 100);
20389
20390    return data;
20391  }
20392
20393  // Function to update cart counter from server
20394  async function updateCartCounter(){
20395    try {
20396      const res = await fetch('/api/cart/get.php', { credentials:'same-origin' });
20397      const data = await res.json().catch(()=>({ok:false}));
20398      if (data.ok && typeof data.items_count === 'number') {
20399        const el = document.querySelector('[data-role="cart-badge"]') || document.getElementById('cartCount');
20400        if (el) {
20401          el.textContent = String(data.items_count);
20402          el.classList.toggle('is-empty', data.items_count <= 0);
20403        }
20404        // Dispatch event
20405        try {
20406          document.dispatchEvent(new CustomEvent('cart:updated', { detail: { items_count: data.items_count } }));
20407        } catch(_){}
20408      }
20409    } catch (err) {
20410      console.log('Could not update cart counter:', err);
20411    }
20412  }
20413
20414  // Function to manually merge guest cart (if needed)
20415  async function mergeGuestCart(){
20416    try {
20417      const res = await fetch('/api/cart/merge-guest.php', {
20418        method: 'POST',
20419        headers: { 
20420          'Content-Type': 'application/json',
20421          'X-CSRF-Token': getCsrfToken()
20422        },
20423        credentials: 'same-origin',
20424        body: JSON.stringify({ csrf_token: getCsrfToken() })
20425      });
20426      const data = await res.json().catch(()=>({ok:false}));
20427      
20428      if (data.ok && data.merged_items > 0) {
20429        console.log(`Guest cart merged: ${data.merged_items} items`);
20430        // Update cart counter after merge
20431        await updateCartCounter();
20432        return data;
20433      }
20434      return data;
20435    } catch (err) {
20436      console.log('Could not merge guest cart:', err);
20437      return { ok: false, error: err.message };
20438    }
20439  }
20440
20441  window.addToCart = addToCart;
20442  window.updateCartCounter = updateCartCounter;
20443  window.mergeGuestCart = mergeGuestCart;
20444
20445  // Optional: auto-bind untuk tombol apapun yang punya data-addtocart
20446  document.addEventListener('click', async (e)=>{
20447    const btn = e.target.closest('[data-addtocart]');
20448    if (!btn) return;
20449    e.preventDefault();
20450    if (btn.disabled) return;
20451
20452    const pid = parseInt(btn.getAttribute('data-pid')||'0',10);
20453    const vid = parseInt(btn.getAttribute('data-vid')||'0',10);
20454    const qty = parseInt(btn.getAttribute('data-qty')||'1',10) || 1;
20455
20456    try {
20457      const data = await addToCart(pid, qty, vid);
20458      if (window.showToast) {
20459        const meta = guessPdMeta();
20460        window.showToast({
20461          type:'success',
20462          title:'Berhasil ditambahkan',
20463          message:`<strong>${esc(meta.name)}</strong>${meta.sku?` · <em>SKU: ${esc(meta.sku)}</em>`:''}<br>Qty: ${qty}`,
20464          duration: 3800,
20465          actions: [
20466            { label:'Lihat Keranjang', onClick: ()=> { window.location.href='/keranjang'; } },
20467            { label:'Lanjut Belanja', variant:'alt', onClick: ()=>{} }
20468          ]
20469        });
20470      }
20471    } catch (err) {
20472      if (window.showToast) {
20473        window.showToast({ type:'warn', title:'Gagal', message: String(err?.message||'Gagal menambahkan'), duration: 4200 });
20474      } else {
20475        console.error(err);
20476      }
20477    }
20478  });
20479})();
20480</script>
vendor: 2 bytes, line 20480
20480
20481<script data-inlined="mega-menu-tiles.js">
20482// Tile Mega Menu — Enhanced 2025 (Stagger animations, product counts, loading states)
20483// Includes safe-area hover handling (no backdrop flicker)
20484
20485(function(){
20486  const trigger   = document.querySelector('[data-mm2="trigger"]');
20487  const panel     = document.querySelector('[data-mm2="panel"]');
20488  const topView   = document.querySelector('[data-mm2="top"]');
20489  const detailView= document.querySelector('[data-mm2="detail"]');
20490  const backdrop  = document.querySelector('[data-mm2="backdrop"]');
20491  if (!trigger || !panel || !topView || !detailView) return;
20492
20493  const apiUrl = '/api/categories_tree.php?top=16&child=18&grand=14';
20494  const isDesktop = () => window.matchMedia('(min-width: 961px)').matches;
20495
20496  let data = [];
20497  let hoverTimer, closeTimer;
20498  let isLoading = false;
20499
20500  const esc = s => (s+'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
20501  
20502  // Mobile close button HTML
20503  const mobileCloseBtn = () => `
20504    <button type="button" class="mm2-mobile-close" data-mm2="close" aria-label="Tutup menu">
20505      <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">
20506        <line x1="18" y1="6" x2="6" y2="18"></line>
20507        <line x1="6" y1="6" x2="18" y2="18"></line>
20508      </svg>
20509    </button>
20510  `;
20511  
20512  // Category-specific gradient colors (16 unique combinations)
20513  const categoryGradients = [
20514    { from: '#e0e7ff', to: '#fff', border: '#c7d2fe', color: '#78350F' },  // Indigo
20515    { from: '#FEF3C7', to: '#fff', border: '#FDE68A', color: '#8B6914' },  // Blue
20516    { from: '#d1fae5', to: '#fff', border: '#a7f3d0', color: '#059669' },  // Green
20517    { from: '#FEF3C7', to: '#fff', border: '#FDE68A', color: '#B45309' },  // Amber
20518    { from: '#fce7f3', to: '#fff', border: '#fbcfe8', color: '#db2777' },  // Pink
20519    { from: '#FEF08A', to: '#fff', border: '#FBBF24', color: '#B45309' },  // Purple
20520    { from: '#FEF3C7', to: '#fff', border: '#FDE68A', color: '#CA8A04' },  // Orange
20521    { from: '#ccfbf1', to: '#fff', border: '#99f6e4', color: '#0d9488' },  // Teal
20522    { from: '#FEF3C7', to: '#fff', border: '#FDE68A', color: '#B45309' },  // Yellow
20523    { from: '#ffe4e6', to: '#fff', border: '#fecdd3', color: '#e11d48' },  // Rose
20524    { from: '#FEF9C3', to: '#fff', border: '#FDE68A', color: '#A16207' },  // Gold
20525    { from: '#FFFBEB', to: '#fff', border: '#FEF08A', color: '#EAB308' },  // Violet
20526    { from: '#dcfce7', to: '#fff', border: '#bbf7d0', color: '#16a34a' },  // Emerald
20527    { from: '#fef2f2', to: '#fff', border: '#fecaca', color: '#dc2626' },  // Red
20528    { from: '#FFFBEB', to: '#fff', border: '#FEF3C7', color: '#CA8A04' },  // Gold-600
20529    { from: '#FFFBEB', to: '#fff', border: '#FEF3C7', color: '#8B6914' },  // Violet-600
20530  ];
20531  
20532  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>`;
20533
20534  // Recent (optional future use)
20535  const REC_KEY='mm2_recent';
20536  const saveRecent=(path,label)=>{ try{
20537    let list=JSON.parse(localStorage.getItem(REC_KEY)||'[]').filter(x=>x&&x.path!==path);
20538    list.unshift({path,label,t:Date.now()}); localStorage.setItem(REC_KEY, JSON.stringify(list.slice(0,8)));
20539  }catch{} };
20540
20541  function isToSafeArea(e){
20542    const t = e && e.relatedTarget ? e.relatedTarget : null;
20543    if (!t) return false;
20544    return (trigger.contains(t) || panel.contains(t) || (backdrop && backdrop.contains(t)));
20545  }
20546
20547  function open(){ 
20548    document.body.classList.add('mm2-open'); 
20549    showTop(); 
20550    // Add entrance animation class
20551    setTimeout(() => panel.classList.add('mm2-animated'), 10);
20552  }
20553  function close(){ 
20554    document.body.classList.remove('mm2-open');
20555    panel.classList.remove('mm2-animated');
20556  }
20557  function showTop(){ 
20558    topView.classList.add('is-active'); 
20559    detailView.classList.remove('is-active'); 
20560  }
20561  
20562  // Loading skeleton
20563  function showLoading() {
20564    const skeletons = Array.from({length: 12}, (_, i) => `
20565      <div class="mm2-card mm2-skeleton" style="animation-delay: ${i * 0.02}s">
20566        <span class="mm2-ico skeleton-box"></span>
20567        <span style="flex: 1;">
20568          <div class="skeleton-line" style="width: 60%; height: 16px; margin-bottom: 6px;"></div>
20569          <div class="skeleton-line" style="width: 40%; height: 12px;"></div>
20570        </span>
20571      </div>
20572    `).join('');
20573    topView.innerHTML = `<div class="mm2-top"><div class="mm2-grid">${skeletons}</div></div>`;
20574  }
20575  
20576  // Count total products in a category tree
20577  function countProducts(node) {
20578    let count = node.product_count || 0;
20579    if (Array.isArray(node.children)) {
20580      node.children.forEach(child => {
20581        count += countProducts(child);
20582      });
20583    }
20584    return count;
20585  }
20586  
20587  // Cache for fetched thumbnails to avoid duplicate requests
20588  const thumbnailCache = new Map();
20589  
20590  // Fetch product thumbnails for a category
20591  function fetchCategoryThumbnails(categoryPath) {
20592    // Check cache first
20593    if (thumbnailCache.has(categoryPath)) {
20594      injectThumbnails(categoryPath, thumbnailCache.get(categoryPath));
20595      return;
20596    }
20597    
20598    const url = `/api/category_products_preview.php?path=${encodeURIComponent(categoryPath)}&limit=2`;
20599    
20600    fetch(url)
20601      .then(r => {
20602        if (!r.ok) throw new Error('Network error');
20603        return r.json();
20604      })
20605      .then(data => {
20606        if (data.success && data.products && data.products.length > 0) {
20607          thumbnailCache.set(categoryPath, data.products);
20608          injectThumbnails(categoryPath, data.products);
20609        } else {
20610          // No products, remove skeleton
20611          removeThumbSkeletons(categoryPath);
20612        }
20613      })
20614      .catch(err => {
20615        console.warn('Failed to load thumbnails for', categoryPath, err);
20616        removeThumbSkeletons(categoryPath);
20617      });
20618  }
20619  
20620  // Inject thumbnails into the DOM
20621  function injectThumbnails(categoryPath, products) {
20622    const links = detailView.querySelectorAll(`[data-cat-path="${categoryPath}"]`);
20623    links.forEach(link => {
20624      const thumbsContainer = link.querySelector('.mm2-thumbs');
20625      if (!thumbsContainer) return;
20626      
20627      const thumbsHtml = products.slice(0, 2).map(p => 
20628        `<img src="${esc(p.img)}" alt="${esc(p.name)}" class="mm2-thumb" title="${esc(p.name)}" loading="lazy">`
20629      ).join('');
20630      
20631      thumbsContainer.innerHTML = thumbsHtml;
20632      thumbsContainer.removeAttribute('data-loading');
20633    });
20634  }
20635  
20636  // Remove skeleton loaders when no products
20637  function removeThumbSkeletons(categoryPath) {
20638    const links = detailView.querySelectorAll(`[data-cat-path="${categoryPath}"]`);
20639    links.forEach(link => {
20640      const thumbsContainer = link.querySelector('.mm2-thumbs');
20641      if (thumbsContainer) {
20642        thumbsContainer.remove();
20643      }
20644    });
20645  }
20646
20647  function showDetail(node){
20648    const title = node.label.split('/')[0];
20649    const children = Array.isArray(node.children) ? node.children : [];
20650    const hasChildren = children.length > 0;
20651
20652    const subnav = hasChildren ? `
20653      <nav class="mm2-subnav" role="tablist">
20654        ${children.map((c2,i)=>{
20655          const t = c2.label.split('/').slice(-1)[0];
20656          return `<button class="mm2-subitem${i===0?' active':''}" role="tab" data-idx="${i}" aria-selected="${i===0?'true':'false'}">${esc(t)}</button>`;
20657        }).join('')}
20658      </nav>` : '';
20659
20660    const subcontent = `<div class="mm2-subcontent" data-mm2="subcontent"></div>`;
20661
20662    detailView.innerHTML = `
20663      <div class="mm2-detail">
20664        <div class="mm2-detail-header">
20665          <button class="mm2-back" type="button" data-mm2="back" aria-label="Kembali">
20666            <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>
20667          </button>
20668          <div class="mm2-detail-title">${esc(title)}</div>
20669        </div>
20670
20671        ${hasChildren ? `
20672          <div class="mm2-detail-layout">
20673            ${subnav}
20674            ${subcontent}
20675          </div>` : `
20676          <div class="mm2-links">
20677            <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)}">
20678              <strong>Lihat semua ${esc(title)}</strong>
20679            </a>
20680          </div>
20681        `}
20682      </div>
20683    `;
20684
20685    topView.classList.remove('is-active');
20686    detailView.classList.add('is-active');
20687
20688    if (hasChildren) {
20689      const subEl  = detailView.querySelector('.mm2-subnav');
20690      const contEl = detailView.querySelector('[data-mm2="subcontent"]');
20691      let activeIdx = 0;
20692
20693      const renderSubgrid = (idx) => {
20694        const c2 = children[idx]; if (!c2) { contEl.innerHTML=''; return; }
20695        const title2 = c2.label.split('/').slice(-1)[0];
20696        const l3 = Array.isArray(c2.children) ? c2.children : [];
20697        const l3Html = l3.map(c3 => `
20698          <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)}">
20699            <span class="mm2-link-text">${esc(c3.label.split('/').slice(-1)[0])}</span>
20700            <span class="mm2-thumbs" data-loading="true">
20701              <span class="mm2-thumb-skeleton"></span>
20702              <span class="mm2-thumb-skeleton"></span>
20703            </span>
20704          </a>`).join('');
20705
20706        contEl.innerHTML = `
20707          <div class="mm2-links">
20708            <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)}">
20709              <strong>Lihat semua ${esc(title2)}</strong>
20710            </a>
20711            ${l3Html}
20712          </div>
20713        `;
20714        
20715        // Fetch product thumbnails for each level-3 category (desktop only)
20716        if (isDesktop()) {
20717          setTimeout(() => {
20718            l3.forEach(c3 => {
20719              fetchCategoryThumbnails(c3.path);
20720            });
20721          }, 100);
20722        }
20723      };
20724
20725      renderSubgrid(activeIdx);
20726
20727      // Desktop: hover ganti konten; Mobile & Desktop: click pilih
20728      subEl.addEventListener('mousemove', (e)=>{
20729        if (!isDesktop()) return;
20730        const it = e.target.closest('.mm2-subitem'); if (!it) return;
20731        const idx = +it.dataset.idx; if (!Number.isInteger(idx) || idx===activeIdx) return;
20732        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.classList.remove('active'));
20733        it.classList.add('active');
20734        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.setAttribute('aria-selected', el===it ? 'true':'false'));
20735        activeIdx = idx;
20736        renderSubgrid(activeIdx);
20737      });
20738
20739      subEl.addEventListener('click', (e)=>{
20740        const it = e.target.closest('.mm2-subitem'); if (!it) return;
20741        const idx = +it.dataset.idx; if (!Number.isInteger(idx)) return;
20742        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.classList.remove('active'));
20743        it.classList.add('active');
20744        subEl.querySelectorAll('.mm2-subitem').forEach(el=>el.setAttribute('aria-selected', el===it ? 'true':'false'));
20745        activeIdx = idx;
20746        renderSubgrid(activeIdx);
20747      });
20748
20749      // Delegate link clicks to add recent
20750      detailView.addEventListener('click', (e)=>{
20751        const nav = e.target.closest('[data-mm2="nav"]');
20752        if (nav) { saveRecent(nav.dataset.path, nav.dataset.label); }
20753      });
20754    }
20755  }
20756
20757  function buildTop(){
20758    const html = data.map((n, idx) => {
20759      const title = n.label.split('/')[0];
20760      const childCount = (n.children||[]).length;
20761      const productCount = countProducts(n);
20762      const gradient = categoryGradients[idx % categoryGradients.length];
20763      
20764      // Dynamic subtitle based on content - shorter for mobile
20765      let sub = '';
20766      if (productCount > 0) {
20767        sub = `${productCount} produk`;
20768        if (childCount > 0) sub += ` · ${childCount} kat`;
20769      } else if (childCount > 0) {
20770        sub = `${childCount} kategori`;
20771      } else {
20772        sub = 'Lihat semua';
20773      }
20774      
20775      const iconStyle = `background: linear-gradient(135deg, ${gradient.from}, ${gradient.to}); border-color: ${gradient.border}; color: ${gradient.color};`;
20776      
20777      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)}">
20778        <span class="mm2-ico" style="${iconStyle}">${icon()}</span>
20779        <span style="flex: 1;">
20780          <div class="mm2-title">${esc(title)}</div>
20781          <div class="mm2-sub">${esc(sub)}</div>
20782        </span>
20783      </a>`;
20784    }).join('');
20785    const closeBtnHtml = !isDesktop() ? mobileCloseBtn() : '';
20786    topView.innerHTML = `<div class="mm2-top">${closeBtnHtml}<div class="mm2-grid">${html}</div></div>`;
20787  }
20788
20789  // Open/close with safe-area
20790  trigger.addEventListener('click', (e)=>{ e.preventDefault(); document.body.classList.contains('mm2-open') ? close() : open(); });
20791  trigger.addEventListener('pointerenter', ()=>{ if (isDesktop()) { clearTimeout(closeTimer); hoverTimer=setTimeout(open, 80); }});
20792  trigger.addEventListener('pointerleave', (e)=>{ if (isDesktop() && !isToSafeArea(e)) { clearTimeout(hoverTimer); closeTimer=setTimeout(close, 200); }});
20793  panel.addEventListener('pointerenter', ()=>{ if (isDesktop()) clearTimeout(closeTimer); });
20794  panel.addEventListener('pointerleave', (e)=>{ if (isDesktop() && !isToSafeArea(e)) closeTimer=setTimeout(close, 200); });
20795  backdrop?.addEventListener('pointerenter', ()=>{ clearTimeout(closeTimer); });
20796  backdrop?.addEventListener('click', close);
20797  document.addEventListener('keydown', (e)=>{ if (e.key==='Escape') close(); });
20798
20799  // Card click → open detail (unless cmd/ctrl to open link)
20800  panel.addEventListener('click', (e)=>{
20801    const closeBtn = e.target.closest('[data-mm2="close"]'); if (closeBtn) { e.preventDefault(); close(); return; }
20802    const back = e.target.closest('[data-mm2="back"]'); if (back) { e.preventDefault(); showTop(); return; }
20803    const card = e.target.closest('[data-mm2="card"]');
20804    if (card && !e.metaKey && !e.ctrlKey) {
20805      e.preventDefault();
20806      const n = data.find(d => d.path === card.dataset.path);
20807      if (n) showDetail(n);
20808    }
20809    const nav = e.target.closest('[data-mm2="nav"]');
20810    if (nav) { saveRecent(nav.dataset.path, nav.dataset.label); }
20811  });
20812
20813  // Swipe-down gesture for mobile (bottom sheet close)
20814  if (!isDesktop()) {
20815    let startY = 0, currentY = 0, isDragging = false, startTime = 0;
20816    const SWIPE_THRESHOLD = 80;
20817    const VELOCITY_THRESHOLD = 0.3;
20818
20819    panel.addEventListener('touchstart', (e) => {
20820      if (!document.body.classList.contains('mm2-open')) return;
20821      const touch = e.touches[0];
20822      const scrollableContent = e.target.closest('.mm2-top, .mm2-detail');
20823      
20824      // Prevent drag if content is scrolled
20825      if (scrollableContent && scrollableContent.scrollTop > 5) return;
20826      
20827      // Only start drag from top area or handle
20828      const rect = panel.getBoundingClientRect();
20829      if (touch.clientY - rect.top > 60) return;
20830      
20831      startY = touch.clientY;
20832      currentY = startY;
20833      startTime = Date.now();
20834      isDragging = true;
20835    }, { passive: true });
20836
20837    panel.addEventListener('touchmove', (e) => {
20838      if (!isDragging) return;
20839      const touch = e.touches[0];
20840      currentY = touch.clientY;
20841      const deltaY = currentY - startY;
20842      
20843      // Only allow downward swipe
20844      if (deltaY > 0) {
20845        const opacity = Math.max(0.4, 1 - (deltaY / 300));
20846        panel.style.transform = `translateX(0) translateY(${deltaY}px)`;
20847        panel.style.opacity = opacity;
20848      }
20849    }, { passive: true });
20850
20851    const endDrag = () => {
20852      if (!isDragging) return;
20853      const deltaY = currentY - startY;
20854      const deltaTime = Date.now() - startTime;
20855      const velocity = deltaY / deltaTime;
20856      
20857      // Close if swiped down enough or fast swipe
20858      if (deltaY > SWIPE_THRESHOLD || velocity > VELOCITY_THRESHOLD) {
20859        close();
20860      }
20861      
20862      // Reset position
20863      panel.style.transform = '';
20864      panel.style.opacity = '';
20865      isDragging = false;
20866    };
20867
20868    panel.addEventListener('touchend', endDrag, { passive: true });
20869    panel.addEventListener('touchcancel', endDrag, { passive: true });
20870  }
20871
20872  // Fetch data with loading state
20873  if (!isLoading) {
20874    isLoading = true;
20875    showLoading();
20876    
20877    fetch(apiUrl, { headers: { 'Accept': 'application/json' }})
20878      .then(r => {
20879        if (!r.ok) throw new Error('Network response was not ok');
20880        return r.json();
20881      })
20882      .then(j => { 
20883        data = Array.isArray(j.data) ? j.data : []; 
20884        if (!data.length) {
20885          topView.innerHTML = `<div class="mm2-top"><div style="text-align:center;padding:40px;color:var(--mm2-fg-muted);">
20886            <p>Tidak ada kategori tersedia</p>
20887          </div></div>`;
20888          return;
20889        }
20890        buildTop();
20891        isLoading = false;
20892      })
20893      .catch(err => {
20894        console.error('Failed to load categories:', err);
20895        topView.innerHTML = `<div class="mm2-top"><div style="text-align:center;padding:40px;color:var(--mm2-fg-muted);">
20896          <p>Gagal memuat kategori. Silakan coba lagi.</p>
20897        </div></div>`;
20898        isLoading = false;
20899      });
20900  }
20901})();
20902
20903// Add skeleton animation styles
20904const style = document.createElement('style');
20905style.textContent = `
20906.mm2-skeleton {
20907  pointer-events: none;
20908  animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
20909}
20910.skeleton-box, .skeleton-line {
20911  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
20912  background-size: 200% 100%;
20913  animation: shimmer 1.5s infinite;
20914  border-radius: 8px;
20915}
20916@keyframes pulse {
20917  0%, 100% { opacity: 1; }
20918  50% { opacity: 0.6; }
20919}
20920@keyframes shimmer {
20921  0% { background-position: 200% 0; }
20922  100% { background-position: -200% 0; }
20923}
20924`;
20925document.head.appendChild(style);
20926</script>
20926
20927    
20927<script data-inlined="benchmark.js">
20928/**
20929 * Benchmark Data Loader
20930 * Handles AJAX fetch and display of benchmark data
20931 */
20932
20933(function() {
20934    'use strict';
20935    
20936    // Wait for DOM ready
20937    if (document.readyState === 'loading') {
20938        document.addEventListener('DOMContentLoaded', initBenchmark);
20939    } else {
20940        initBenchmark();
20941    }
20942    
20943    function initBenchmark() {
20944        const section = document.querySelector('.pd-benchmark-section');
20945        if (!section) return;
20946        
20947        const button = document.getElementById('btn-load-benchmark');
20948        const content = document.getElementById('benchmark-content');
20949        const productId = section.dataset.productId;
20950        
20951        if (!button || !content || !productId) return;
20952        
20953        let isLoading = false;
20954        let dataLoaded = false;
20955        
20956        button.addEventListener('click', function() {
20957            if (isLoading || dataLoaded) return;
20958            
20959            loadBenchmarkData(productId, button, content);
20960        });
20961    }
20962    
20963    function loadBenchmarkData(productId, button, container) {
20964        // Update button state
20965        button.disabled = true;
20966        button.classList.add('loading');
20967        const originalText = button.querySelector('.btn-text').textContent;
20968        button.querySelector('.btn-text').textContent = 'Memuat data...';
20969        
20970        // Show loading state
20971        container.style.display = 'block';
20972        container.innerHTML = `
20973            <div class="benchmark-loading">
20974                <div class="benchmark-loading-spinner"></div>
20975                <p>Mengambil data benchmark...</p>
20976            </div>
20977        `;
20978        
20979        // Fetch data
20980        fetch('/api/benchmark/fetch.php?product_id=' + productId)
20981            .then(function(response) {
20982                if (!response.ok) {
20983                    throw new Error('HTTP error ' + response.status);
20984                }
20985                return response.json();
20986            })
20987            .then(function(result) {
20988                if (result.success && result.data) {
20989                    renderBenchmarkData(result.data, container);
20990                    button.style.display = 'none'; // Hide button after successful load
20991                } else {
20992                    throw new Error(result.error || 'Data tidak tersedia');
20993                }
20994            })
20995            .catch(function(error) {
20996                console.error('Benchmark fetch error:', error);
20997                container.innerHTML = `
20998                    <div class="benchmark-error">
20999                        <p><strong>⚠️ Data Benchmark Tidak Tersedia</strong></p>
21000                        <p style="font-size: 14px; margin-top: 8px;">
21001                            ${error.message || 'Terjadi kesalahan saat memuat data'}
21002                        </p>
21003                        <button type="button" onclick="location.reload()" 
21004                                style="margin-top: 16px; padding: 8px 20px; background: #EAB308; color: #fff; border: none; border-radius: 6px; cursor: pointer;">
21005                            Coba Lagi
21006                        </button>
21007                    </div>
21008                `;
21009                
21010                // Reset button
21011                button.disabled = false;
21012                button.classList.remove('loading');
21013                button.querySelector('.btn-text').textContent = originalText;
21014            });
21015    }
21016    
21017    function renderBenchmarkData(data, container) {
21018        let html = '<div class="benchmark-data-wrapper">';
21019        
21020        // Chipset info if available
21021        if (data.chipset) {
21022            html += `
21023                <div style="background: linear-gradient(135deg, #EAB308 0%, #8B6914 100%); color: white; padding: 10px 16px; border-radius: 8px; margin-bottom: 12px;">
21024                    <div style="font-size: 11px; opacity: 0.85; margin-bottom: 2px;">Chipset</div>
21025                    <div style="font-size: 15px; font-weight: 600;">${escapeHtml(data.chipset)}</div>
21026                </div>
21027            `;
21028        }
21029        
21030        // Main benchmark scores
21031        if (data.antutu || data.geekbench_multi || data.overall_score) {
21032            html += '<div class="benchmark-data-grid">';
21033            
21034            if (data.antutu) {
21035                html += `
21036                    <div class="benchmark-card">
21037                        <div class="benchmark-card-label">ANTUTU</div>
21038                        <div class="benchmark-card-value large">${formatNumber(data.antutu)}</div>
21039                        ${getScoreBadge(data.antutu, 'antutu')}
21040                    </div>
21041                `;
21042            }
21043            
21044            if (data.geekbench_multi) {
21045                html += `
21046                    <div class="benchmark-card">
21047                        <div class="benchmark-card-label">GEEKBENCH MULTI</div>
21048                        <div class="benchmark-card-value">${formatNumber(data.geekbench_multi)}</div>
21049                        ${data.geekbench_single ? '<div class="benchmark-card-subtitle">Single: ' + formatNumber(data.geekbench_single) + '</div>' : ''}
21050                    </div>
21051                `;
21052            }
21053            
21054            if (data.gaming_score) {
21055                html += `
21056                    <div class="benchmark-card">
21057                        <div class="benchmark-card-label">GAMING</div>
21058                        <div class="benchmark-card-value">${data.gaming_score}<span style="font-size: 14px; opacity: 0.6;">/100</span></div>
21059                    </div>
21060                `;
21061            }
21062            
21063            if (data.battery_score) {
21064                html += `
21065                    <div class="benchmark-card">
21066                        <div class="benchmark-card-label">BATTERY</div>
21067                        <div class="benchmark-card-value">${data.battery_score}<span style="font-size: 14px; opacity: 0.6;">/100</span></div>
21068                    </div>
21069                `;
21070            }
21071            
21072            if (data.overall_score) {
21073                html += `
21074                    <div class="benchmark-card">
21075                        <div class="benchmark-card-label">OVERALL</div>
21076                        <div class="benchmark-card-value">${data.overall_score}<span style="font-size: 14px; opacity: 0.6;">/100</span></div>
21077                    </div>
21078                `;
21079            }
21080            
21081            html += '</div>';
21082        }
21083        
21084        // AI Performance Description
21085        if (data.notes) {
21086            html += `
21087                <div style="background: #f8f9fa; border-left: 3px solid #EAB308; padding: 10px 14px; border-radius: 6px; margin-top: 12px;">
21088                    <div style="font-size: 11px; font-weight: 600; color: #EAB308; margin-bottom: 6px;">💡 Analisis Performa</div>
21089                    <div style="font-size: 13px; color: #4a5568; line-height: 1.5;">${escapeHtml(data.notes)}</div>
21090                </div>
21091            `;
21092        }
21093        
21094        // Attribution
21095        html += `
21096            <div class="benchmark-attribution">
21097                <p style="font-size: 11px; margin: 8px 0 0 0; color: #888;">Data benchmark dianalisis menggunakan AI dari spesifikasi produk</p>
21098                ${data.fetched_at ? '<p style="font-size: 10px; margin: 4px 0 0 0; color: #aaa;">Terakhir diperbarui: ' + formatDate(data.fetched_at) + '</p>' : ''}
21099            </div>
21100        `;
21101        
21102        html += '</div>';
21103        
21104        container.innerHTML = html;
21105    }
21106    
21107    function formatNumber(num) {
21108        if (!num) return '-';
21109        return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
21110    }
21111    
21112    function escapeHtml(text) {
21113        if (!text) return '';
21114        const div = document.createElement('div');
21115        div.textContent = text;
21116        return div.innerHTML;
21117    }
21118    
21119    function formatDate(dateStr) {
21120        try {
21121            const date = new Date(dateStr);
21122            const options = { year: 'numeric', month: 'long', day: 'numeric' };
21123            return date.toLocaleDateString('id-ID', options);
21124        } catch (e) {
21125            return dateStr;
21126        }
21127    }
21128    
21129    function getScoreBadge(score, type) {
21130        let category = '';
21131        let label = '';
21132        
21133        if (type === 'antutu') {
21134            if (score >= 500000) {
21135                category = 'excellent';
21136                label = 'Flagship';
21137            } else if (score >= 350000) {
21138                category = 'good';
21139                label = 'High-End';
21140            } else if (score >= 200000) {
21141                category = 'average';
21142                label = 'Mid-Range';
21143            } else {
21144                category = 'average';
21145                label = 'Entry-Level';
21146            }
21147        }
21148        
21149        if (label) {
21150            return '<div class="benchmark-score-badge ' + category + '">' + label + '</div>';
21151        }
21152        
21153        return '';
21154    }
21155})();
21156
21157</script>
21157
21158  
21158<script data-inlined="product-compare.js">
21159(function() {
21160  'use strict';
21161  
21162  // State management
21163  let compareList = [];
21164  let isAnalyzing = false;
21165  let currentAnalysis = null;
21166  
21167  // DOM elements
21168  const compareBtn = document.getElementById('compareBtn');
21169  const compareSidebar = document.getElementById('compareSidebar');
21170  const compareOverlay = document.getElementById('compareOverlay');
21171  const compareClose = document.getElementById('compareClose');
21172  const compareModal = document.getElementById('aiCompareModal');
21173  const compareModalClose = document.getElementById('aiCompareModalClose');
21174  
21175  // Initialize
21176  document.addEventListener('DOMContentLoaded', async function() {
21177    await initializeCompare();
21178    setupEventListeners();
21179  });
21180  
21181  /**
21182   * Initialize compare functionality
21183   */
21184  async function initializeCompare() {
21185    // Load compare list first before setting up buttons
21186    await loadCompareList();
21187    
21188    // Setup product detail compare button if exists
21189    const productCompareBtn = document.getElementById('pd-compare-btn');
21190    
21191    if (productCompareBtn) {
21192      setupProductCompareButton(productCompareBtn);
21193    }
21194    
21195    // Check for existing compare list and update UI
21196    if (compareList.length > 0) {
21197      // Only update badge and buttons, no sticky bar
21198    }
21199  }
21200  
21201  /**
21202   * Setup event listeners
21203   */
21204  function setupEventListeners() {
21205    // Navigation compare button
21206    if (compareBtn) {
21207      compareBtn.addEventListener('click', toggleCompareSidebar);
21208    }
21209    
21210    // Sidebar controls
21211    if (compareClose) {
21212      compareClose.addEventListener('click', closeCompareSidebar);
21213    }
21214    
21215    if (compareOverlay) {
21216      compareOverlay.addEventListener('click', closeCompareSidebar);
21217    }
21218    
21219    // Sidebar actions
21220    const clearAllBtn = document.getElementById('compareClearAll');
21221    if (clearAllBtn) {
21222      clearAllBtn.addEventListener('click', clearAllProducts);
21223    }
21224    
21225    const analyzeBtn = document.getElementById('compareAnalyze');
21226    if (analyzeBtn) {
21227      analyzeBtn.addEventListener('click', startAIAnalysis);
21228    }
21229    
21230    // Modal controls
21231    if (compareModalClose) {
21232      compareModalClose.addEventListener('click', closeModal);
21233    }
21234    
21235    const modalBackdrop = compareModal?.querySelector('.modal-backdrop');
21236    if (modalBackdrop) {
21237      modalBackdrop.addEventListener('click', closeModal);
21238    }
21239    
21240    // Modal actions
21241    const retryBtn = document.getElementById('aiRetry');
21242    if (retryBtn) {
21243      retryBtn.addEventListener('click', startAIAnalysis);
21244    }
21245    
21246    // Removed share and add to cart buttons - replaced with AI disclaimer
21247  }
21248  
21249  /**
21250   * Setup product detail compare button
21251   */
21252  function setupProductCompareButton(button) {
21253    const productId = parseInt(button.dataset.productId);
21254    if (!productId) return;
21255    
21256    // Check if product is already in compare list
21257    updateProductCompareButtonState(button, productId);
21258    
21259    button.addEventListener('click', async function() {
21260      // Get current variant ID (if any)
21261      const variantIdElement = document.getElementById('pd-variant-id');
21262      const variantId = variantIdElement ? parseInt(variantIdElement.value) || 0 : 0;
21263      
21264      // Update button's variant data for current selection
21265      button.dataset.variantId = variantId.toString();
21266      
21267      await toggleProductInCompare(productId, button, variantId);
21268    });
21269  }
21270  
21271  /**
21272   * Toggle product in compare list
21273   */
21274  async function toggleProductInCompare(productId, button, variantId = 0) {
21275    // Check by product ID (variants of same product treated as same item)
21276    const isInCompare = compareList.some(p => parseInt(p.id) === parseInt(productId));
21277    
21278    try {
21279      if (isInCompare) {
21280        await removeFromCompare(productId);
21281        showToast('Produk dihapus dari perbandingan', 'success');
21282      } else {
21283        await addToCompare(productId, variantId);
21284        showToast('Produk ditambahkan ke perbandingan', 'success');
21285        
21286        // Trigger fly-to-compare animation
21287        triggerFlyToCompareAnimation(button);
21288      }
21289      
21290      updateProductCompareButtonState(button, productId);
21291      updateCompareBadge();
21292      updateAllCompareButtons();
21293      
21294    } catch (error) {
21295      showToast(error.message || 'Terjadi kesalahan', 'error');
21296    }
21297  }
21298  
21299  /**
21300   * Update product compare button state
21301   */
21302  function updateProductCompareButtonState(button, productId) {
21303    // Ensure both IDs are compared as integers
21304    const isInCompare = compareList.some(p => parseInt(p.id) === parseInt(productId));
21305    button.classList.toggle('added', isInCompare);
21306    
21307    // Handle visibility of text elements
21308    const compareText = button.querySelector('.compare-text');
21309    const compareRemove = button.querySelector('.compare-remove');
21310    const compareNote = document.getElementById('compare-note');
21311    
21312    if (compareText) {
21313      compareText.style.display = isInCompare ? 'none' : 'inline';
21314    }
21315    
21316    if (compareRemove) {
21317      if (isInCompare) {
21318        compareRemove.removeAttribute('hidden');
21319        compareRemove.style.display = 'flex';
21320      } else {
21321        compareRemove.setAttribute('hidden', '');
21322        compareRemove.style.display = 'none';
21323      }
21324    }
21325    
21326    // Update info note
21327    if (compareNote) {
21328      if (isInCompare) {
21329        const currentCount = compareList.length;
21330        compareNote.textContent = `Produk sudah ada di daftar perbandingan (${currentCount}/5). Klik tombol untuk menghapus.`;
21331      } else {
21332        compareNote.textContent = 'Bandingkan hingga 5 produk dengan bantuan AI';
21333      }
21334    }
21335    
21336    // Inline compare preview removed - keeping it simple
21337  }
21338  
21339  /**
21340   * Add product to compare list
21341   */
21342  async function addToCompare(productId, variantId = 0) {
21343    const requestData = { product_id: productId };
21344    if (variantId > 0) {
21345      requestData.variant_id = variantId;
21346    }
21347    
21348    const response = await fetch('/api/compare/add.php', {
21349      method: 'POST',
21350      headers: {
21351        'Content-Type': 'application/json',
21352      },
21353      body: JSON.stringify(requestData),
21354      credentials: 'same-origin'
21355    });
21356    
21357    const result = await response.json();
21358    
21359    if (!result.success) {
21360      throw new Error(result.error || 'Gagal menambahkan produk');
21361    }
21362    
21363    // Send Google Analytics event for add to compare
21364    if (typeof gtag === 'function') {
21365      gtag('event', 'add_to_compare', {
21366        event_category: 'Product Compare',
21367        event_label: 'Product Added to Compare',
21368        product_id: productId,
21369        variant_id: variantId || 0,
21370        custom_map: {
21371          custom_parameter_1: 'compare_add_product'
21372        }
21373      });
21374    }
21375    
21376    await loadCompareList();
21377    return result;
21378  }
21379  
21380  /**
21381   * Remove product from compare list
21382   */
21383  async function removeFromCompare(productId) {
21384    const response = await fetch('/api/compare/remove.php', {
21385      method: 'POST',
21386      headers: {
21387        'Content-Type': 'application/json',
21388      },
21389      body: JSON.stringify({ product_id: productId }),
21390      credentials: 'same-origin'
21391    });
21392    
21393    const result = await response.json();
21394    
21395    if (!result.success) {
21396      throw new Error(result.error || 'Gagal menghapus produk');
21397    }
21398    
21399    // Send Google Analytics event for remove from compare
21400    if (typeof gtag === 'function') {
21401      gtag('event', 'remove_from_compare', {
21402        event_category: 'Product Compare',
21403        event_label: 'Product Removed from Compare',
21404        product_id: productId,
21405        custom_map: {
21406          custom_parameter_1: 'compare_remove_product'
21407        }
21408      });
21409    }
21410    
21411    await loadCompareList();
21412    return result;
21413  }
21414  
21415  /**
21416   * Clear all products from compare list
21417   */
21418  async function clearAllProducts() {
21419    if (compareList.length === 0) return;
21420    
21421    // Show confirmation modal
21422    const confirmed = await showConfirmModal({
21423      title: 'Hapus Semua Produk',
21424      message: 'Apakah Anda yakin ingin menghapus semua produk dari daftar perbandingan?',
21425      confirmText: 'Hapus Semua',
21426      cancelText: 'Batal',
21427      type: 'danger'
21428    });
21429    
21430    if (!confirmed) return;
21431    
21432    try {
21433      const response = await fetch('/api/compare/clear.php', {
21434        method: 'POST',
21435        credentials: 'same-origin'
21436      });
21437      
21438      const result = await response.json();
21439      
21440      if (!result.success) {
21441        throw new Error(result.error || 'Gagal menghapus produk');
21442      }
21443      
21444      // Send Google Analytics event for clear all products
21445      if (typeof gtag === 'function') {
21446        gtag('event', 'compare_clear_all', {
21447          event_category: 'Product Compare',
21448          event_label: 'Clear All Products',
21449          products_count: compareList.length,
21450          custom_map: {
21451            custom_parameter_1: 'compare_clear_action'
21452          }
21453        });
21454      }
21455      
21456      await loadCompareList();
21457      showToast('Semua produk dihapus dari perbandingan', 'success');
21458      
21459    } catch (error) {
21460      showToast(error.message || 'Terjadi kesalahan', 'error');
21461    }
21462  }
21463  
21464  /**
21465   * Load compare list from server
21466   */
21467  async function loadCompareList() {
21468    try {
21469      const response = await fetch('/api/compare/get.php', {
21470        credentials: 'same-origin'
21471      });
21472      
21473      const result = await response.json();
21474      
21475      if (result.success) {
21476        compareList = result.products || [];
21477        updateCompareSidebar();
21478        updateCompareBadge();
21479        updateAllCompareButtons();
21480      }
21481      
21482    } catch (error) {
21483      // Silently handle error - compare functionality will work with empty list
21484    }
21485  }
21486  
21487  /**
21488   * Update compare sidebar content
21489   */
21490  function updateCompareSidebar() {
21491    const emptyState = document.getElementById('compareEmpty');
21492    const listContainer = document.getElementById('compareList');
21493    const actionsContainer = document.getElementById('compareActions');
21494    const countElement = document.getElementById('compareCountSidebar');
21495    
21496    if (!emptyState || !listContainer || !actionsContainer || !countElement) return;
21497    
21498    // Update count
21499    countElement.textContent = `(${compareList.length})`;
21500    
21501    if (compareList.length === 0) {
21502      emptyState.style.display = 'block';
21503      listContainer.style.display = 'none';
21504      actionsContainer.style.display = 'none';
21505    } else {
21506      emptyState.style.display = 'none';
21507      listContainer.style.display = 'block';
21508      actionsContainer.style.display = 'flex';
21509      
21510      // Render product list
21511      listContainer.innerHTML = compareList.map(product => {
21512        // Display variant name if available
21513        let displayName = product.name;
21514        if (product.combination_name) {
21515          displayName += ` (${product.combination_name})`;
21516        }
21517        
21518        // Use variant price if available
21519        let displayPrice = product.price;
21520        if (product.variant_price !== undefined && product.variant_price !== null) {
21521          displayPrice = product.variant_price;
21522        }
21523        
21524        return `
21525          <div class="compare-item" data-product-id="${product.id}">
21526            <div class="compare-item-image">
21527              <img src="${product.image_url}" alt="${escapeHtml(displayName)}" loading="lazy">
21528            </div>
21529            <div class="compare-item-info">
21530              <div class="compare-item-name">${escapeHtml(displayName)}</div>
21531              <div class="compare-item-price">${formatPrice(displayPrice)}</div>
21532            </div>
21533            <button class="compare-item-remove" data-product-id="${product.id}" aria-label="Hapus">
21534              <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
21535                <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"/>
21536              </svg>
21537            </button>
21538          </div>
21539        `;
21540      }).join('');
21541      
21542      // Setup event listeners for remove buttons
21543      setupRemoveButtons();
21544      
21545      // Update analyze button state
21546      const analyzeBtn = document.getElementById('compareAnalyze');
21547      if (analyzeBtn) {
21548        analyzeBtn.disabled = compareList.length < 2;
21549      }
21550    }
21551  }
21552  
21553  /**
21554   * Setup remove buttons event listeners
21555   */
21556  function setupRemoveButtons() {
21557    const removeButtons = document.querySelectorAll('.compare-item-remove');
21558    removeButtons.forEach(button => {
21559      button.addEventListener('click', async (e) => {
21560        e.preventDefault();
21561        const productId = parseInt(button.dataset.productId);
21562        if (productId) {
21563          await removeProduct(productId);
21564        }
21565      });
21566    });
21567  }
21568
21569  /**
21570   * Update all compare buttons on the page
21571   */
21572  function updateAllCompareButtons() {
21573    // Update product detail compare button
21574    const productBtn = document.getElementById('pd-compare-btn');
21575    if (productBtn) {
21576      const productId = parseInt(productBtn.dataset.productId);
21577      if (productId) {
21578        updateProductCompareButtonState(productBtn, productId);
21579      }
21580    }
21581    
21582    // Update any other compare buttons on the page
21583    const allCompareButtons = document.querySelectorAll('[data-product-id][class*="compare"]');
21584    allCompareButtons.forEach(button => {
21585      const productId = parseInt(button.dataset.productId);
21586      if (productId && button.id !== 'pd-compare-btn') {
21587        updateProductCompareButtonState(button, productId);
21588      }
21589    });
21590  }
21591
21592  /**
21593   * Update compare badge
21594   */
21595  function updateCompareBadge() {
21596    const badge = document.getElementById('compareCount');
21597    if (!badge) return;
21598    
21599    const oldCount = parseInt(badge.textContent) || 0;
21600    const newCount = compareList.length;
21601    
21602    badge.textContent = newCount;
21603    badge.classList.toggle('active', newCount > 0);
21604    
21605    // Animate badge if count increased
21606    if (newCount > oldCount) {
21607      animateCompareBadge();
21608      highlightCompareButton();
21609    }
21610    
21611    // Sticky bar removed - keeping only fly-to-navbar animation
21612  }
21613  
21614  /**
21615   * Animate compare badge when new product added
21616   */
21617  function animateCompareBadge() {
21618    const badge = document.getElementById('compareCount');
21619    if (!badge) return;
21620    
21621    badge.style.transform = 'scale(1.3)';
21622    badge.style.transition = 'transform 0.3s ease';
21623    
21624    setTimeout(() => {
21625      badge.style.transform = 'scale(1)';
21626    }, 300);
21627  }
21628  
21629  /**
21630   * Highlight compare button temporarily
21631   */
21632  function highlightCompareButton() {
21633    const compareBtn = document.getElementById('compareBtn');
21634    if (!compareBtn) return;
21635    
21636    compareBtn.classList.add('pulse-highlight');
21637    
21638    setTimeout(() => {
21639      compareBtn.classList.remove('pulse-highlight');
21640    }, 3000);
21641  }
21642  
21643  /**
21644   * Trigger fly-to-compare animation
21645   */
21646  function triggerFlyToCompareAnimation(sourceButton) {
21647    if (!sourceButton) {
21648      return;
21649    }
21650    
21651    // Check if mobile view (bottom nav) or desktop (top nav)
21652    const isMobile = window.innerWidth <= 768;
21653    
21654    // Get source position
21655    const sourceRect = sourceButton.getBoundingClientRect();
21656    
21657    // Calculate target position
21658    let targetTop, targetLeft;
21659    
21660    if (isMobile) {
21661      // Mobile: fly to fixed bottom position (bottom nav area)
21662      // Use fixed coordinates regardless of bottom nav visibility
21663      targetTop = window.innerHeight - 80; // Just above bottom nav area
21664      targetLeft = window.innerWidth / 2 - 30; // Center horizontally
21665    } else {
21666      // Desktop: fly to top navbar compare button
21667      const targetElement = document.getElementById('compareBtn');
21668      if (!targetElement) {
21669        return;
21670      }
21671      const targetRect = targetElement.getBoundingClientRect();
21672      targetTop = targetRect.top + 10;
21673      targetLeft = targetRect.left + 10;
21674    }
21675    
21676    // Get product image for animation - try multiple selectors
21677    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"]');
21678    
21679    // Create animated element
21680    const flyingElement = document.createElement('div');
21681    flyingElement.className = 'flying-product';
21682    
21683    if (productImage) {
21684      flyingElement.innerHTML = `<img src="${productImage.src}" alt="Flying product">`;
21685      flyingElement.querySelector('img').style.cssText = `
21686        width: 100%;
21687        height: 100%;
21688        object-fit: cover;
21689        border-radius: 8px;
21690        box-shadow: 0 4px 12px rgba(0,0,0,0.3);
21691      `;
21692    } else {
21693      // Fallback: colored div with icon
21694      flyingElement.innerHTML = `<div class="flying-fallback">📦</div>`;
21695      flyingElement.querySelector('.flying-fallback').style.cssText = `
21696        width: 100%;
21697        height: 100%;
21698        background: #EAB308;
21699        border-radius: 8px;
21700        display: flex;
21701        align-items: center;
21702        justify-content: center;
21703        font-size: 24px;
21704        box-shadow: 0 4px 12px rgba(0,0,0,0.3);
21705      `;
21706    }
21707    
21708    flyingElement.style.cssText = `
21709      position: fixed;
21710      top: ${sourceRect.top}px;
21711      left: ${sourceRect.left}px;
21712      width: 60px;
21713      height: 60px;
21714      z-index: 9999;
21715      pointer-events: none;
21716      opacity: 1;
21717      transform: scale(1);
21718    `;
21719    
21720    document.body.appendChild(flyingElement);
21721    
21722    // Use Web Animations API for more reliable animation
21723    flyingElement.animate([
21724      // Keyframe 1: Initial state
21725      {
21726        top: `${sourceRect.top}px`,
21727        left: `${sourceRect.left}px`,
21728        transform: 'scale(1)',
21729        opacity: 1
21730      },
21731      // Keyframe 2: Final state
21732      {
21733        top: `${targetTop}px`,
21734        left: `${targetLeft}px`,
21735        transform: 'scale(0.3)',
21736        opacity: 0
21737      }
21738    ], {
21739      duration: 800,
21740      easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
21741      fill: 'forwards'
21742    });
21743    
21744    // Clean up after animation completes
21745    setTimeout(() => {
21746      if (flyingElement.parentNode) {
21747        flyingElement.parentNode.removeChild(flyingElement);
21748      }
21749    }, 850);
21750  }
21751  
21752  // Sticky compare bar functions removed - using only fly-to-navbar animation
21753  
21754  /**
21755   * Toggle compare sidebar
21756   */
21757  function toggleCompareSidebar() {
21758    if (!compareSidebar) return;
21759    
21760    const isOpen = compareSidebar.classList.contains('open');
21761    
21762    if (isOpen) {
21763      closeCompareSidebar();
21764    } else {
21765      openCompareSidebar();
21766    }
21767  }
21768  
21769  /**
21770   * Open compare sidebar
21771   */
21772  function openCompareSidebar() {
21773    if (!compareSidebar || !compareOverlay) return;
21774    
21775    compareSidebar.classList.add('open');
21776    compareOverlay.classList.add('active');
21777    document.body.style.overflow = 'hidden';
21778    
21779    // Send Google Analytics event for compare sidebar open
21780    if (typeof gtag === 'function') {
21781      gtag('event', 'compare_sidebar_open', {
21782        event_category: 'Product Compare',
21783        event_label: 'Compare Sidebar Opened',
21784        products_count: compareList.length,
21785        custom_map: {
21786          custom_parameter_1: 'compare_sidebar_view'
21787        }
21788      });
21789    }
21790  }
21791  
21792  /**
21793   * Close compare sidebar
21794   */
21795  function closeCompareSidebar() {
21796    if (!compareSidebar || !compareOverlay) return;
21797    
21798    compareSidebar.classList.remove('open');
21799    compareOverlay.classList.remove('active');
21800    document.body.style.overflow = '';
21801  }
21802  
21803  /**
21804   * Start AI analysis
21805   */
21806  async function startAIAnalysis() {
21807    if (compareList.length < 2) {
21808      showToast('Minimal 2 produk diperlukan untuk analisis AI', 'warning');
21809      return;
21810    }
21811    
21812    if (isAnalyzing) return;
21813    
21814    // Send Google Analytics event for AI Analysis start
21815    if (typeof gtag === 'function') {
21816      gtag('event', 'ai_analysis_start', {
21817        event_category: 'Product Compare',
21818        event_label: 'AI Analysis Button Click',
21819        products_count: compareList.length,
21820        product_ids: compareList.map(p => p.id).join(','),
21821        custom_map: {
21822          custom_parameter_1: 'compare_ai_analysis'
21823        }
21824      });
21825    }
21826    
21827    // Check if products are in similar categories
21828    const mismatchInfo = await checkProductCategoryMismatch();
21829    if (mismatchInfo.shouldWarn) {
21830      const categoryList = Array.from(mismatchInfo.categories).join(', ');
21831      const confirmed = await showConfirmModal({
21832        title: 'Produk Berbeda Kategori',
21833        message: `Produk yang Anda pilih berasal dari kategori yang berbeda: ${categoryList}. AI tetap bisa menganalisis, tapi perbandingan mungkin kurang relevan. Lanjutkan analisis?`,
21834        confirmText: 'Ya, Analisis Tetap',
21835        cancelText: 'Batal',
21836        type: 'warning'
21837      });
21838      
21839      if (!confirmed) return;
21840    }
21841    
21842    openModal();
21843    showLoadingState();
21844    isAnalyzing = true;
21845    
21846    try {
21847      const response = await fetch('/api/compare/ai-analysis.php', {
21848        method: 'POST',
21849        credentials: 'same-origin'
21850      });
21851      
21852      const result = await response.json();
21853      
21854      if (!result.success) {
21855        throw new Error(result.error || 'Analisis AI gagal');
21856      }
21857      
21858      currentAnalysis = result;
21859      showAnalysisResults(result);
21860      
21861      // Send Google Analytics event for successful AI Analysis
21862      if (typeof gtag === 'function') {
21863        gtag('event', 'ai_analysis_success', {
21864          event_category: 'Product Compare',
21865          event_label: 'AI Analysis Completed',
21866          products_count: compareList.length,
21867          analysis_type: 'product_comparison',
21868          custom_map: {
21869            custom_parameter_1: 'compare_ai_success'
21870          }
21871        });
21872      }
21873      
21874    } catch (error) {
21875      showErrorState(error.message);
21876      
21877      // Send Google Analytics event for failed AI Analysis
21878      if (typeof gtag === 'function') {
21879        gtag('event', 'ai_analysis_error', {
21880          event_category: 'Product Compare',
21881          event_label: 'AI Analysis Failed',
21882          products_count: compareList.length,
21883          error_message: error.message,
21884          custom_map: {
21885            custom_parameter_1: 'compare_ai_error'
21886          }
21887        });
21888      }
21889    } finally {
21890      isAnalyzing = false;
21891    }
21892  }
21893  
21894  /**
21895   * Show modal
21896   */
21897  function openModal() {
21898    if (!compareModal) return;
21899    compareModal.classList.add('open');
21900    document.body.style.overflow = 'hidden';
21901  }
21902  
21903  /**
21904   * Close modal
21905   */
21906  function closeModal() {
21907    if (!compareModal) return;
21908    compareModal.classList.remove('open');
21909    document.body.style.overflow = '';
21910  }
21911  
21912  /**
21913   * Show loading state
21914   */
21915  function showLoadingState() {
21916    const loading = document.getElementById('aiLoading');
21917    const error = document.getElementById('aiError');
21918    const results = document.getElementById('aiResults');
21919    
21920    if (loading) loading.style.display = 'block';
21921    if (error) error.hidden = true;
21922    if (results) results.hidden = true;
21923  }
21924  
21925  /**
21926   * Show error state
21927   */
21928  function showErrorState(message) {
21929    const loading = document.getElementById('aiLoading');
21930    const error = document.getElementById('aiError');
21931    const results = document.getElementById('aiResults');
21932    const errorMessage = document.getElementById('aiErrorMessage');
21933    
21934    if (loading) loading.style.display = 'none';
21935    if (error) error.hidden = false;
21936    if (results) results.hidden = true;
21937    if (errorMessage) errorMessage.textContent = message;
21938  }
21939  
21940  /**
21941   * Show analysis results
21942   */
21943  function showAnalysisResults(analysis) {
21944    const loading = document.getElementById('aiLoading');
21945    const error = document.getElementById('aiError');
21946    const results = document.getElementById('aiResults');
21947    
21948    if (loading) loading.style.display = 'none';
21949    if (error) error.hidden = true;
21950    if (results) results.hidden = false;
21951    
21952    const data = analysis.data;
21953    
21954    // Update summary
21955    const summaryElement = document.getElementById('aiSummary');
21956    if (summaryElement) summaryElement.textContent = data.summary || '';
21957    
21958    const winnerProduct = document.getElementById('winnerProduct');
21959    const winnerReason = document.getElementById('winnerReason');
21960    if (winnerProduct && winnerReason && data.winner_overall) {
21961      const winner = compareList.find(p => parseInt(p.id) === parseInt(data.winner_overall));
21962      winnerProduct.textContent = winner ? winner.name : 'Tidak diketa
21962hui';
21963      winnerReason.textContent = data.winner_reason || '';
21964    }
21965    
21966    // Update comparison table
21967    updateComparisonTable(data.comparison_table);
21968    
21969    // Update detailed analysis
21970    updateDetailedAnalysis(data.detailed_analysis);
21971    
21972    // Update recommendations
21973    updateRecommendations(data.recommendations);
21974    
21975    // Update buying tips
21976    updateBuyingTips(data.buying_tips);
21977  }
21978  
21979  /**
21980   * Update comparison table
21981   */
21982  function updateComparisonTable(comparisonData) {
21983    const container = document.getElementById('comparisonTable');
21984    if (!container || !comparisonData?.categories) return;
21985    
21986    // First, create product header row with names
21987    const productHeaderHtml = `
21988      <div class="comparison-category">
21989        <div class="category-header">Produk</div>
21990        <div class="category-products">
21991          ${compareList.map(product => `
21992            <div class="product-score">
21993              <div class="product-name-header">
21994                <strong>${escapeHtml(product.name)}</strong>
21995              </div>
21996              <div class="product-price">
21997                ${formatPrice(product.price)}
21998              </div>
21999            </div>
22000          `).join('')}
22001        </div>
22002      </div>
22003    `;
22004    
22005    // Then create comparison categories
22006    const categoriesHtml = comparisonData.categories.map(category => `
22007      <div class="comparison-category">
22008        <div class="category-header">${escapeHtml(category.name)}</div>
22009        <div class="category-products">
22010          ${compareList.map(product => {
22011            const productData = category.products?.[product.id];
22012            return `
22013              <div class="product-score">
22014                <div class="score-value">
22015                  <span class="score-stars">${'★'.repeat(productData?.score || 0)}${'☆'.repeat(5 - (productData?.score || 0))}</span>
22016                  <span>${productData?.score || 0}/5</span>
22017                </div>
22018                <div class="score-note">${escapeHtml(productData?.note || '')}</div>
22019              </div>
22020            `;
22021          }).join('')}
22022        </div>
22023      </div>
22024    `).join('');
22025    
22026    container.innerHTML = productHeaderHtml + categoriesHtml;
22027  }
22028  
22029  /**
22030   * Update detailed analysis
22031   */
22032  function updateDetailedAnalysis(analysisData) {
22033    const container = document.getElementById('detailedAnalysis');
22034    if (!container || !analysisData) return;
22035    
22036    container.innerHTML = compareList.map(product => {
22037      const productAnalysis = analysisData[product.id];
22038      if (!productAnalysis) return '';
22039      
22040      // Use product name from API response if available, fallback to local name
22041      const productName = productAnalysis.product_name || product.name;
22042      
22043      return `
22044        <div class="product-analysis">
22045          <h5>${escapeHtml(productName)}</h5>
22046          
22047          ${productAnalysis.strengths ? `
22048            <div class="analysis-section">
22049              <h6 class="strengths">✅ Kelebihan</h6>
22050              <ul>
22051                ${productAnalysis.strengths.map(strength => 
22052                  `<li>${escapeHtml(strength)}</li>`
22053                ).join('')}
22054              </ul>
22055            </div>
22056          ` : ''}
22057          
22058          ${productAnalysis.weaknesses ? `
22059            <div class="analysis-section">
22060              <h6 class="weaknesses">❌ Kekurangan</h6>
22061              <ul>
22062                ${productAnalysis.weaknesses.map(weakness => 
22063                  `<li>${escapeHtml(weakness)}</li>`
22064                ).join('')}
22065              </ul>
22066            </div>
22067          ` : ''}
22068          
22069          ${productAnalysis.best_for ? `
22070            <div class="analysis-section">
22071              <h6 class="best-for">🎯 Cocok Untuk</h6>
22072              <p class="best-for-text">${escapeHtml(productAnalysis.best_for)}</p>
22073            </div>
22074          ` : ''}
22075        </div>
22076      `;
22077    }).filter(Boolean).join('');
22078  }
22079  
22080  /**
22081   * Update recommendations
22082   */
22083  function updateRecommendations(recommendations) {
22084    if (!recommendations) return;
22085    
22086    const budgetEl = document.getElementById('budgetRecommendation');
22087    const performanceEl = document.getElementById('performanceRecommendation');
22088    const balancedEl = document.getElementById('balancedRecommendation');
22089    
22090    if (budgetEl && recommendations.budget_conscious) {
22091      const product = compareList.find(p => parseInt(p.id) === parseInt(recommendations.budget_conscious));
22092      budgetEl.textContent = product ? product.name : 'Tidak tersedia';
22093    }
22094    
22095    if (performanceEl && recommendations.performance_focused) {
22096      const product = compareList.find(p => parseInt(p.id) === parseInt(recommendations.performance_focused));
22097      performanceEl.textContent = product ? product.name : 'Tidak tersedia';
22098    }
22099    
22100    if (balancedEl && recommendations.balanced) {
22101      const product = compareList.find(p => parseInt(p.id) === parseInt(recommendations.balanced));
22102      balancedEl.textContent = product ? product.name : 'Tidak tersedia';
22103    }
22104  }
22105  
22106  /**
22107   * Check if products are from different categories
22108   */
22109  async function checkProductCategoryMismatch() {
22110    if (compareList.length < 2) {
22111      return { shouldWarn: false, categories: new Set() };
22112    }
22113    
22114    // Get the root categories from each product
22115    const rootCategories = new Set();
22116    const productCategories = [];
22117    
22118    for (const product of compareList) {
22119      // Extract root category from the product's category_path if available
22120      const rootCategory = await getProductRootCategory(product);
22121      if (rootCategory) {
22122        rootCategories.add(rootCategory);
22123        productCategories.push({
22124          product: product.name,
22125          category: rootCategory
22126        });
22127      }
22128    }
22129    
22130    // If we have more than 1 different root categories, show warning
22131    const shouldWarn = rootCategories.size > 1 && compareList.length >= 2;
22132    
22133    return {
22134      shouldWarn: shouldWarn,
22135      categories: rootCategories,
22136      productCategories: productCategories
22137    };
22138  }
22139  
22140  /**
22141   * Get product root category from category_path or fallback to name detection
22142   */
22143  async function getProductRootCategory(product) {
22144    // First try to get from category_path if available
22145    if (product.category_path) {
22146      const rootCategory = product.category_path.split('/')[0];
22147      return rootCategory;
22148    }
22149    
22150    // If no category_path, try to fetch from product detail or use name detection
22151    const detectedCategory = detectProductCategory(product.name);
22152    return detectedCategory;
22153  }
22154
22155  /**
22156   * Detect product category from name (fallback method)
22157   */
22158  function detectProductCategory(productName) {
22159    const name = productName.toLowerCase();
22160    
22161    // Map keywords to actual root categories from the API structure
22162    const categoryMappings = {
22163      // Komputer & Laptop
22164      'Komputer & Laptop': ['laptop', 'notebook', 'macbook', 'thinkpad', 'gaming laptop', 'desktop', 'pc', 'mini pc', 'all-in-one', 'server'],
22165      
22166      // Printer & Scanner  
22167      'Printer & Scanner': ['printer', 'epson printer', 'canon printer', 'hp printer', 'brother printer', 'scanner', 'laser printer', 'inkjet', 'dot matrix'],
22168      
22169      // Gadget
22170      'Gadget': ['smartphone', 'iphone', 'samsung phone', 'xiaomi', 'oppo', 'vivo', 'realme', 'tablet', 'ipad', 'android tablet'],
22171      
22172      // Periferal
22173      'Periferal': ['monitor', 'lcd monitor', 'led monitor', 'gaming monitor', 'keyboard', 'mouse', 'gaming keyboard', 'mechanical keyboard', 'webcam'],
22174      
22175      // Audio
22176      'Audio': ['headset', 'headphone', 'earphone', 'gaming headset', 'speaker', 'mikrofon', 'sound card'],
22177      
22178      // Jaringan
22179      'Jaringan': ['router', 'wifi', 'access point', 'switch', 'modem', 'network'],
22180      
22181      // Penyimpanan
22182      'Penyimpanan': ['ssd', 'hdd', 'nvme', 'solid state', 'hard disk', 'flash drive', 'usb drive', 'memory card'],
22183      
22184      // Komponen PC
22185      'Komponen PC': ['ram', 'memory', 'ddr4', 'ddr5', 'processor', 'cpu', 'intel', 'amd', 'motherboard', 'graphics card', 'gpu', 'psu', 'power supply', 'casing'],
22186      
22187      // UPS & Power
22188      'UPS & Power': ['ups', 'uninterruptible', 'stabilizer', 'power supply'],
22189      
22190      // Proyektor
22191      'Proyektor': ['proyektor', 'projector', 'layar proyektor'],
22192      
22193      // Security System
22194      'Security System': ['cctv', 'security camera', 'surveillance'],
22195      
22196      // Aksesoris
22197      'Aksesoris': ['charger laptop', 'adaptor', 'converter', 'dock', 'hub', 'kabel', 'cable', 'power bank', 'tas laptop'],
22198      
22199      // Software
22200      'Software': ['software', 'lisensi', 'license', 'antivirus'],
22201      
22202      // Perlengkapan Kantor
22203      'Perlengkapan Kantor': ['fingerprint', 'finger print', 'atk', 'alat tulis']
22204    };
22205    
22206    // Check which root category matches
22207    for (const [rootCategory, keywords] of Object.entries(categoryMappings)) {
22208      for (const keyword of keywords) {
22209        if (name.includes(keyword)) {
22210          return rootCategory;
22211        }
22212      }
22213    }
22214    
22215    return null;
22216  }
22217
22218  /**
22219   * Update buying tips
22220   */
22221  function updateBuyingTips(tips) {
22222    const container = document.getElementById('buyingTips');
22223    if (!container || !Array.isArray(tips)) return;
22224    
22225    container.innerHTML = tips.map(tip => 
22226      `<li>${escapeHtml(tip)}</li>`
22227    ).join('');
22228  }
22229  
22230  // Removed share and cart functions - replaced with AI disclaimer in UI
22231  
22232  /**
22233   * Remove product from compare (public method)
22234   */
22235  async function removeProduct(productId) {
22236    try {
22237      await removeFromCompare(productId);
22238      showToast('Produk dihapus dari perbandingan', 'success');
22239      
22240      // Update all compare buttons
22241      updateAllCompareButtons();
22242      
22243    } catch (error) {
22244      showToast(error.message || 'Terjadi kesalahan', 'error');
22245    }
22246  }
22247  
22248  /**
22249   * Utility functions
22250   */
22251  function escapeHtml(text) {
22252    const div = document.createElement('div');
22253    div.textContent = text;
22254    return div.innerHTML;
22255  }
22256  
22257  function formatPrice(price) {
22258    return 'Rp ' + parseInt(price).toLocaleString('id-ID');
22259  }
22260  
22261  /**
22262   * Show confirmation modal using the existing modal.css design
22263   */
22264  function showConfirmModal(options = {}) {
22265    return new Promise((resolve) => {
22266      const {
22267        title = 'Konfirmasi',
22268        message = 'Apakah Anda yakin?',
22269        confirmText = 'Ya',
22270        cancelText = 'Tidak',
22271        type = 'warning'
22272      } = options;
22273      
22274      // Create modal HTML
22275      const modalHTML = `
22276        <div class="modal-overlay" id="confirmModal">
22277          <div class="modal-dialog">
22278            <div class="modal-header">
22279              <h3 class="modal-title">
22280                <div class="modal-icon ${type}">
22281                  ${getModalIcon(type)}
22282                </div>
22283                ${escapeHtml(title)}
22284              </h3>
22285            </div>
22286            <div class="modal-body">
22287              <p class="modal-message">${escapeHtml(message)}</p>
22288              <div class="modal-actions">
22289                <button type="button" class="modal-btn modal-btn-secondary" id="confirmCancel">
22290                  ${escapeHtml(cancelText)}
22291                </button>
22292                <button type="button" class="modal-btn modal-btn-${type === 'danger' ? 'danger' : 'primary'}" id="confirmOk">
22293                  ${escapeHtml(confirmText)}
22294                </button>
22295              </div>
22296            </div>
22297          </div>
22298        </div>
22299      `;
22300      
22301      // Add to DOM
22302      document.body.insertAdjacentHTML('beforeend', modalHTML);
22303      const modal = document.getElementById('confirmModal');
22304      const cancelBtn = document.getElementById('confirmCancel');
22305      const okBtn = document.getElementById('confirmOk');
22306      
22307      // Show modal
22308      requestAnimationFrame(() => {
22309        modal.classList.add('show');
22310      });
22311      
22312      // Handle events
22313      function cleanup() {
22314        modal.remove();
22315      }
22316      
22317      function handleCancel() {
22318        modal.classList.remove('show');
22319        setTimeout(() => {
22320          cleanup();
22321          resolve(false);
22322        }, 200);
22323      }
22324      
22325      function handleConfirm() {
22326        modal.classList.remove('show');
22327        setTimeout(() => {
22328          cleanup();
22329          resolve(true);
22330        }, 200);
22331      }
22332      
22333      cancelBtn.addEventListener('click', handleCancel);
22334      okBtn.addEventListener('click', handleConfirm);
22335      
22336      // Close on overlay click
22337      modal.addEventListener('click', (e) => {
22338        if (e.target === modal) {
22339          handleCancel();
22340        }
22341      });
22342      
22343      // Close on escape
22344      document.addEventListener('keydown', function escapeHandler(e) {
22345        if (e.key === 'Escape') {
22346          document.removeEventListener('keydown', escapeHandler);
22347          handleCancel();
22348        }
22349      });
22350      
22351      // Focus on confirm button
22352      setTimeout(() => okBtn.focus(), 100);
22353    });
22354  }
22355  
22356  /**
22357   * Get icon for modal type
22358   */
22359  function getModalIcon(type) {
22360      const icons = {
22361        danger: '🚨 ',
22362        warning: '⚠️',
22363        success: '✅',
22364        info: 'ℹ️'
22365      };
22366      return icons[type] || icons.info;
22367    }
22368
22369
22370  function showToast(message, type = 'info') {
22371    // Integration with existing toast system or create simple one
22372    
22373    // Simple alert fallback - replace with proper toast system
22374    if (type === 'error') {
22375      alert('Error: ' + message);
22376    }
22377  }
22378  
22379  // Inline compare preview functions removed - keeping it simple and clean
22380  
22381  /**
22382   * Get current product ID from page
22383   */
22384  function getCurrentProductId() {
22385    const button = document.getElementById('pd-compare-btn');
22386    return button ? parseInt(button.dataset.productId) : null;
22387  }
22388  
22389  // hideStickyCompareBar function removed - no longer using sticky bar
22390
22391  // Public API
22392  window.productCompare = {
22393    removeProduct: removeProduct,
22394    addToCompare: addToCompare,
22395    loadCompareList: loadCompareList,
22396    getCompareList: function() { return compareList; },
22397    openSidebar: openCompareSidebar,
22398    startAnalysis: startAIAnalysis,
22399    // Helper function to add from external sources with variant support
22400    addProductWithVariant: async function(productId, variantId = 0) {
22401      return await addToCompare(productId, variantId);
22402    }
22403  };
22404
22405})();
22406
22407
22408
22409</script>
vendor: 2 bytes, line 22409
22409
22410<script data-inlined="product-card-actions.js">
22411/**
22412 * Product Card Quick Actions
22413 * Bottom sheet menu for quick add to cart & compare
22414 * Integrates with existing product-compare.js and cart.js
22415 */
22416
22417(function() {
22418  'use strict';
22419  
22420  let activeSheet = null;
22421  let activeProductData = null;
22422  
22423  // DOM elements (will be created dynamically)
22424  let overlay = null;
22425  let bottomSheet = null;
22426  
22427  /**
22428   * Initialize on DOM ready
22429   */
22430  document.addEventListener('DOMContentLoaded', function() {
22431    createBottomSheet();
22432    attachEventListeners();
22433  });
22434  
22435  /**
22436   * Create bottom sheet HTML structure
22437   */
22438  function createBottomSheet() {
22439    // Create overlay
22440    overlay = document.createElement('div');
22441    overlay.className = 'pca-overlay';
22442    overlay.addEventListener('click', closeBottomSheet);
22443    
22444    // Create bottom sheet
22445    bottomSheet = document.createElement('div');
22446    bottomSheet.className = 'pca-bottom-sheet';
22447    bottomSheet.innerHTML = `
22448      <div class="pca-sheet-handle"></div>
22449      <div class="pca-sheet-product" id="pca-product-preview">
22450        <!-- Product info will be inserted here -->
22451      </div>
22452      <div class="pca-sheet-actions">
22453        <button type="button" class="pca-action-item cart" id="pca-add-cart">
22454          <svg class="pca-action-icon" viewBox="0 0 24 24" fill="currentColor">
22455            <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"/>
22456          </svg>
22457          <div class="pca-action-content">
22458            <div class="pca-action-label">Tambah ke Keranjang</div>
22459            <div class="pca-action-desc">Lanjut belanja produk lain</div>
22460          </div>
22461        </button>
22462        
22463        <button type="button" class="pca-action-item compare" id="pca-add-compare">
22464          <svg class="pca-action-icon" viewBox="0 0 24 24" fill="currentColor">
22465            <path d="M21,9L17,5V8H10V10H17V13M7,11L3,15L7,19V16H14V14H7V11Z"/>
22466          </svg>
22467          <div class="pca-action-content">
22468            <div class="pca-action-label">Bandingkan Produk</div>
22469            <div class="pca-action-desc">Analisis dengan AI</div>
22470          </div>
22471          <span class="pca-action-badge compare" id="pca-compare-count" style="display:none;">0</span>
22472        </button>
22473      </div>
22474    `;
22475    
22476    // Append to body
22477    document.body.appendChild(overlay);
22478    document.body.appendChild(bottomSheet);
22479    
22480    // Setup action buttons
22481    document.getElementById('pca-add-cart').addEventListener('click', handleAddToCart);
22482    document.getElementById('pca-add-compare').addEventListener('click', handleAddToCompare);
22483    
22484    // Prevent clicks inside bottom sheet from closing it
22485    bottomSheet.addEventListener('click', function(e) {
22486      e.stopPropagation();
22487    });
22488  }
22489  
22490  /**
22491   * Attach event listeners to all 3-dot buttons
22492   */
22493  function attachEventListeners() {
22494    // Use event delegation for dynamically loaded product cards
22495    document.addEventListener('click', function(e) {
22496      const btn = e.target.closest('.prod-card-actions-btn');
22497      if (btn) {
22498        e.preventDefault();
22499        e.stopPropagation();
22500        handleActionsButtonClick(btn);
22501      }
22502    });
22503    
22504    // Handle ESC key
22505    document.addEventListener('keydown', function(e) {
22506      if (e.key === 'Escape' && activeSheet) {
22507        closeBottomSheet();
22508      }
22509    });
22510  }
22511  
22512  /**
22513   * Handle 3-dot button click
22514   */
22515  function handleActionsButtonClick(button) {
22516    // Get product ID directly from button
22517    const productId = parseInt(button.dataset.productId) || 0;
22518    if (!productId) {
22519      console.error('Product ID not found on button:', button);
22520      showToast('Data produk tidak ditemukan', 'error');
22521      return;
22522    }
22523    
22524    // Find parent card for additional data
22525    const card = button.closest('.prod-card, .pa-card');
22526    if (!card) {
22527      console.error('Product card not found');
22528      showToast('Data produk tidak ditemukan', 'error');
22529      return;
22530    }
22531    
22532    // Extract product data from card
22533    const productData = extractProductData(card, productId);
22534    if (!productData) {
22535      showToast('Data produk tidak ditemukan', 'error');
22536      return;
22537    }
22538    
22539    activeProductData = productData;
22540    openBottomSheet(productData);
22541  }
22542  
22543  /**
22544   * Extract product data from card element
22545   */
22546  function extractProductData(card, productId) {
22547    try {
22548      // Product ID already passed from button
22549      if (!productId) {
22550        console.error('Product ID not provided');
22551        return null;
22552      }
22553      // Product ID already passed from button
22554      if (!productId) {
22555        console.error('Product ID not provided');
22556        return null;
22557      }
22558      
22559      const id = productId;
22560      
22561      // Get product name
22562      const nameEl = card.querySelector('.prod-name, .title');
22563      const name = nameEl ? nameEl.textContent.trim() : 'Produk';
22564      
22565      // Get product image
22566      const imgEl = card.querySelector('.thumb img, img');
22567      const image = imgEl ? imgEl.src : '/assets//img/placeholder.png';
22568      
22569      // Get product price and detect variants
22570      const priceEl = card.querySelector('.prod-price-final, .final, .prod-price');
22571      let price = 0;
22572      let hasVariants = false;
22573      let priceDisplay = '';
22574      
22575      if (priceEl) {
22576        // Get text content and clean it
22577        let priceText = priceEl.textContent.trim();
22578        priceDisplay = priceText;
22579        
22580        // Check if it's a range (indicates product has variants)
22581        if (priceText.includes('-')) {
22582          hasVariants = true;
22583          // Take the first price for calculation
22584          priceText = priceText.split('-')[0].trim();
22585        }
22586        
22587        // Remove all non-numeric characters
22588        priceText = priceText.replace(/[^0-9]/g, '');
22589        price = parseInt(priceText) || 0;
22590      }
22591      
22592      // Get stock status
22593      const stockDot = card.querySelector('.stock-dot');
22594      let stockStatus = 'unknown';
22595      let stockLabel = 'Cek stok';
22596      
22597      if (stockDot) {
22598        if (stockDot.classList.contains('ready')) {
22599          stockStatus = 'in';
22600          stockLabel = 'Stok tersedia';
22601        } else if (stockDot.classList.contains('low')) {
22602          stockStatus = 'low';
22603          stockLabel = 'Stok terbatas';
22604        } else if (stockDot.classList.contains('out')) {
22605          stockStatus = 'out';
22606          stockLabel = 'Stok habis';
22607        }
22608      }
22609      
22610      // Get category path (if available)
22611      const categoryPath = card.dataset.category || '';
22612      
22613      // Get URL
22614      let url = '#';
22615      if (card.tagName === 'A') {
22616        url = card.href;
22617      } else {
22618        const link = card.querySelector('a[href*="/product/"]');
22619        if (link) url = link.href;
22620      }
22621      
22622      return {
22623        id: id,
22624        name: name,
22625        image: image,
22626        price: price,
22627        priceDisplay: priceDisplay,
22628        hasVariants: hasVariants,
22629        stockStatus: stockStatus,
22630        stockLabel: stockLabel,
22631        categoryPath: categoryPath,
22632        url: url
22633      };
22634    } catch (error) {
22635      console.error('Error extracting product data:', error);
22636      return null;
22637    }
22638  }
22639  
22640  /**
22641   * Open bottom sheet
22642   */
22643  function openBottomSheet(productData) {
22644    if (!overlay || !bottomSheet) return;
22645    
22646    // Update product preview
22647    updateProductPreview(productData);
22648    
22649    // Update cart button based on variant status
22650    updateCartButtonForVariant(productData);
22651    
22652    // Update compare button state
22653    updateCompareButtonState(productData.id);
22654    
22655    // Show overlay and sheet
22656    overlay.classList.add('active');
22657    bottomSheet.classList.add('active');
22658    document.body.style.overflow = 'hidden';
22659    
22660    activeSheet = true;
22661    
22662    // Google Analytics
22663    if (typeof gtag === 'function') {
22664      gtag('event', 'quick_actions_open', {
22665        event_category: 'Product Card',
22666        event_label: 'Quick Actions Menu Opened',
22667        product_id: productData.id,
22668        product_name: productData.name
22669      });
22670    }
22671  }
22672  
22673  /**
22674   * Close bottom sheet
22675   */
22676  function closeBottomSheet() {
22677    if (!overlay || !bottomSheet) return;
22678    
22679    overlay.classList.remove('active');
22680    bottomSheet.classList.remove('active');
22681    document.body.style.overflow = '';
22682    
22683    activeSheet = false;
22684    activeProductData = null;
22685  }
22686  
22687  /**
22688   * Update product preview in bottom sheet
22689   */
22690  function updateProductPreview(productData) {
22691    const preview = document.getElementById('pca-product-preview');
22692    if (!preview) return;
22693    
22694    // Use full price display (shows range for variants)
22695    const priceDisplay = productData.priceDisplay || formatPrice(productData.price);
22696    
22697    preview.innerHTML = `
22698      <img src="${escapeHtml(productData.image)}" 
22699           alt="${escapeHtml(productData.name)}" 
22700           class="pca-product-image"
22701           loading="lazy">
22702      <div class="pca-product-info">
22703        <h4 class="pca-product-name">${escapeHtml(productData.name)}</h4>
22704        <p class="pca-product-price">${escapeHtml(priceDisplay)}</p>
22705        <p class="pca-product-stock ${productData.stockStatus}">${productData.stockLabel}</p>
22706      </div>
22707    `;
22708  }
22709  
22710  /**
22711   * Update cart button for variant products
22712   */
22713  function updateCartButtonForVariant(productData) {
22714    const cartBtn = document.getElementById('pca-add-cart');
22715    if (!cartBtn) return;
22716    
22717    if (productData.hasVariants) {
22718      // Product has variants - change to "View Detail"
22719      cartBtn.dataset.hasVariants = 'true';
22720      cartBtn.dataset.productUrl = productData.url;
22721      
22722      // Update icon to eye/external
22723      const iconSvg = cartBtn.querySelector('svg');
22724      if (iconSvg) {
22725        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"/>';
22726      }
22727      
22728      // Update text
22729      const label = cartBtn.querySelector('.pca-action-label');
22730      const desc = cartBtn.querySelector('.pca-action-desc');
22731      if (label) label.textContent = 'Lihat Detail Produk';
22732      if (desc) desc.textContent = 'Pilih varian terlebih dahulu';
22733      
22734    } else {
22735      // Single product - normal cart button
22736      cartBtn.dataset.hasVariants = 'false';
22737      
22738      // Restore cart icon
22739      const iconSvg = cartBtn.querySelector('svg');
22740      if (iconSvg) {
22741        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"/>';
22742      }
22743      
22744      // Restore text
22745      const label = cartBtn.querySelector('.pca-action-label');
22746      const desc = cartBtn.querySelector('.pca-action-desc');
22747      if (label) label.textContent = 'Tambah ke Keranjang';
22748      if (desc) desc.textContent = 'Lanjut belanja produk lain';
22749    }
22750  }
22751  
22752  /**
22753   * Update compare button state
22754   */
22755  function updateCompareButtonState(productId) {
22756    const compareBtn = document.getElementById('pca-add-compare');
22757    const compareBadge = document.getElementById('pca-compare-count');
22758    
22759    if (!compareBtn) return;
22760    
22761    // Check if product is in compare list (using global compareList if available)
22762    let isInCompare = false;
22763    let compareCount = 0;
22764    
22765    // Try to get compare list from product-compare.js via window.productCompare
22766    if (typeof window.productCompare !== 'undefined' && typeof window.productCompare.getCompareList === 'function') {
22767      const compareList = window.productCompare.getCompareList();
22768      compareCount = compareList.length;
22769      isInCompare = compareList.some(p => parseInt(p.id) === parseInt(productId));
22770    } else {
22771      // Fallback: check badge count
22772      const mainBadge = document.getElementById('compareCount');
22773      if (mainBadge) {
22774        compareCount = parseInt(mainBadge.textContent) || 0;
22775      }
22776    }
22777    
22778    // Update button state
22779    if (isInCompare) {
22780      compareBtn.classList.add('active');
22781      compareBtn.querySelector('.pca-action-label').textContent = 'Hapus dari Perbandingan';
22782      compareBtn.querySelector('.pca-action-desc').textContent = 'Sudah ada di daftar';
22783    } else {
22784      compareBtn.classList.remove('active');
22785      compareBtn.querySelector('.pca-action-label').textContent = 'Bandingkan Produk';
22786      compareBtn.querySelector('.pca-action-desc').textContent = 'Analisis dengan AI';
22787    }
22788    
22789    // Update badge
22790    if (compareBadge && compareCount > 0) {
22791      compareBadge.textContent = compareCount;
22792      compareBadge.style.display = 'inline-flex';
22793    } else if (compareBadge) {
22794      compareBadge.style.display = 'none';
22795    }
22796  }
22797  
22798  /**
22799   * Handle add to cart
22800   */
22801  async function handleAddToCart() {
22802    if (!activeProductData) return;
22803    
22804    const button = document.getElementById('pca-add-cart');
22805    if (!button) return;
22806    
22807    // If product has variants, redirect to detail page
22808    if (activeProductData.hasVariants) {
22809      window.location.href = activeProductData.url;
22810      return;
22811    }
22812    
22813    // Check stock
22814    if (activeProductData.stockStatus === 'out') {
22815      showToast('Produk sedang habis', 'warning');
22816      return;
22817    }
22818    
22819    // Show loading state
22820    button.classList.add('loading');
22821    button.disabled = true;
22822    
22823    // CSRF Token Helper
22824    function getCsrfToken() {
22825      return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
22826    }
22827    
22828    try {
22829      // Call cart API with proper format
22830      const response = await fetch('/api/cart/add.php', {
22831        method: 'POST',
22832        headers: {
22833          'Content-Type': 'application/json',
22834          'X-CSRF-Token': getCsrfToken()
22835        },
22836        body: JSON.stringify({
22837          csrf_token: getCsrfToken(),
22838          product_id: activeProductData.id,
22839          qty: 1, // Use 'qty' not 'quantity'
22840          variant_id: 0, // Default variant
22841          meta: {
22842            name: activeProductData.name,
22843            image_url: activeProductData.image
22844          }
22845        }),
22846        credentials: 'same-origin'
22847      });
22848      
22849      const result = await response.json();
22850      
22851      // Check both response status and result.ok
22852      if (!response.ok || !result.ok) {
22853        throw new Error(result.error || result.message || 'Gagal menambahkan ke keranjang');
22854      }
22855      
22856      // Success animation
22857      button.classList.remove('loading');
22858      button.classList.add('success');
22859      
22860      // Update cart badge
22861      updateCartBadge();
22862
22863      // PlazaAnalytics tracking
22864      if (window.PlazaAnalytics) {
22865        const _slug = (activeProductData.url || '').replace(/.*\/product\//, '');
22866        window.PlazaAnalytics.track('add_to_cart', { slug: _slug });
22867      }
22868
22869      // Show success toast
22870      showToast('Produk ditambahkan ke keranjang', 'success');
22871      
22872      // Google Analytics
22873      if (typeof gtag === 'function') {
22874        gtag('event', 'add_to_cart', {
22875          event_category: 'Ecommerce',
22876          event_label: 'Quick Add to Cart',
22877          items: [{
22878            item_id: activeProductData.id,
22879            item_name: activeProductData.name,
22880            price: activeProductData.price,
22881            quantity: 1
22882          }]
22883        });
22884      }
22885      
22886      // Close sheet after short delay
22887      setTimeout(() => {
22888        closeBottomSheet();
22889        button.classList.remove('success');
22890        button.disabled = false;
22891      }, 1000);
22892      
22893    } catch (error) {
22894      console.error('Add to cart error:', error);
22895      button.classList.remove('loading');
22896      button.disabled = false;
22897      showToast(error.message || 'Terjadi kesalahan', 'error');
22898    }
22899  }
22900  
22901  /**
22902   * Handle add to compare
22903   */
22904  async function handleAddToCompare() {
22905    if (!activeProductData) return;
22906    
22907    const button = document.getElementById('pca-add-compare');
22908    if (!button) return;
22909    
22910    // Check if already in compare
22911    const isActive = button.classList.contains('active');
22912    
22913    // Check 5-product limit before adding
22914    if (!isActive) {
22915      const mainBadge = document.getElementById('compareCount');
22916      const currentCount = mainBadge ? parseInt(mainBadge.textContent) || 0 : 0;
22917      
22918      if (currentCount >= 5) {
22919        showToast('Maksimal 5 produk untuk perbandingan', 'warning');
22920        return;
22921      }
22922    }
22923    
22924    // Show loading state
22925    button.classList.add('loading');
22926    button.disabled = true;
22927    
22928    try {
22929      if (isActive) {
22930        // Remove from compare
22931        await removeFromCompare(activeProductData.id);
22932        showToast('Produk dihapus dari perbandingan', 'success');
22933      } else {
22934        // Add to compare
22935        await addToCompare(activeProductData.id);
22936        showToast('Produk ditambahkan ke perbandingan', 'success');
22937        
22938        // Trigger fly animation
22939        triggerFlyToCompareAnimation();
22940      }
22941      
22942      // Success animation
22943      button.classList.remove('loading');
22944      button.classList.add('success');
22945      
22946      // Update compare button state
22947      updateCompareButtonState(activeProductData.id);
22948      
22949      // Close sheet after short delay
22950      setTimeout(() => {
22951        closeBottomSheet();
22952        button.classList.remove('success');
22953        button.disabled = false;
22954      }, 800);
22955      
22956    } catch (error) {
22957      console.error('Compare error:', error);
22958      button.classList.remove('loading');
22959      button.disabled = false;
22960      showToast(error.message || 'Terjadi kesalahan', 'error');
22961    }
22962  }
22963  
22964  /**
22965   * Add to compare (calls existing API)
22966   */
22967  async function addToCompare(productId) {
22968    const response = await fetch('/api/compare/add.php', {
22969      method: 'POST',
22970      headers: {
22971        'Content-Type': 'application/json',
22972      },
22973      body: JSON.stringify({
22974        product_id: productId,
22975        variant_id: 0
22976      }),
22977      credentials: 'same-origin'
22978    });
22979    
22980    const result = await response.json();
22981    
22982    if (!result.success) {
22983      throw new Error(result.error || 'Gagal menambahkan produk');
22984    }
22985    
22986    // Trigger compare list reload if function exists
22987    if (typeof window.productCompare !== 'undefined' && typeof window.productCompare.loadCompareList === 'function') {
22988      await window.productCompare.loadCompareList();
22989    }
22990    
22991    return result;
22992  }
22993  
22994  /**
22995   * Remove from compare (calls existing API)
22996   */
22997  async function removeFromCompare(productId) {
22998    const response = await fetch('/api/compare/remove.php', {
22999      method: 'POST',
23000      headers: {
23001        'Content-Type': 'application/json',
23002      },
23003      body: JSON.stringify({
23004        product_id: productId
23005      }),
23006      credentials: 'same-origin'
23007    });
23008    
23009    const result = await response.json();
23010    
23011    if (!result.success) {
23012      throw new Error(result.error || 'Gagal menghapus produk');
23013    }
23014    
23015    // Trigger compare list reload if function exists
23016    if (typeof window.productCompare !== 'undefined' && typeof window.productCompare.loadCompareList === 'function') {
23017      await window.productCompare.loadCompareList();
23018    }
23019    
23020    return result;
23021  }
23022  
23023  /**
23024   * Update cart badge
23025   */
23026  function updateCartBadge() {
23027    // Try to call global cart update function if exists
23028    if (typeof window.updateCartBadge === 'function') {
23029      window.updateCartBadge();
23030    } else {
23031      // Fallback: increment badge manually
23032      const badge = document.getElementById('cartCount');
23033      if (badge) {
23034        const currentCount = parseInt(badge.textContent) || 0;
23035        badge.textContent = currentCount + 1;
23036        badge.classList.add('active');
23037      }
23038    }
23039  }
23040  
23041  /**
23042   * Trigger fly to compare animation
23043   */
23044  function triggerFlyToCompareAnimation() {
23045    const compareBtn = document.getElementById('compareBtn');
23046    if (!compareBtn || !activeProductData) return;
23047    
23048    // Create flying element
23049    const flyingElement = document.createElement('div');
23050    flyingElement.className = 'flying-product';
23051    flyingElement.style.cssText = `
23052      position: fixed;
23053      width: 60px;
23054      height: 60px;
23055      z-index: 99999;
23056      pointer-events: none;
23057    `;
23058    
23059    flyingElement.innerHTML = `
23060      <img src="${activeProductData.image}" 
23061           style="width:100%;height:100%;object-fit:cover;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.3);">
23062    `;
23063    
23064    // Position at bottom sheet
23065    const sheetRect = bottomSheet.getBoundingClientRect();
23066    const targetRect = compareBtn.getBoundingClientRect();
23067    
23068    flyingElement.style.top = `${sheetRect.top + 20}px`;
23069    flyingElement.style.left = `${sheetRect.left + 20}px`;
23070    
23071    document.body.appendChild(flyingElement);
23072    
23073    // Animate to target
23074    requestAnimationFrame(() => {
23075      flyingElement.style.transition = 'all 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94)';
23076      flyingElement.style.top = `${targetRect.top + 10}px`;
23077      flyingElement.style.left = `${targetRect.left + 10}px`;
23078      flyingElement.style.transform = 'scale(0.3)';
23079      flyingElement.style.opacity = '0';
23080    });
23081    
23082    // Clean up
23083    setTimeout(() => {
23084      if (flyingElement.parentNode) {
23085        flyingElement.parentNode.removeChild(flyingElement);
23086      }
23087    }, 800);
23088  }
23089  
23090  /**
23091   * Format price as Rupiah
23092   */
23093  function formatPrice(price) {
23094    if (!price || price === 0) return 'Rp 0';
23095    return 'Rp ' + parseInt(price).toLocaleString('id-ID');
23096  }
23097  
23098  /**
23099   * Escape HTML
23100   */
23101  function escapeHtml(text) {
23102    const div = document.createElement('div');
23103    div.textContent = text;
23104    return div.innerHTML;
23105  }
23106  
23107  /**
23108   * Show toast notification (uses global showToast if available)
23109   */
23110  function showToast(message, type = 'info') {
23111    if (typeof window.showToast === 'function') {
23112      window.showToast({
23113        title: type === 'success' ? 'Berhasil' : type === 'error' ? 'Gagal' : 'Info',
23114        message: message,
23115        type: type,
23116        duration: 3000
23117      });
23118    } else {
23119      // Fallback: console log
23120      console.log(`[${type.toUpperCase()}] ${message}`);
23121    }
23122  }
23123  
23124  /**
23125   * Expose functions for external access
23126   */
23127  window.ProductCardActions = {
23128    openSheet: openBottomSheet,
23129    closeSheet: closeBottomSheet,
23130    updateCompareState: updateCompareButtonState
23131  };
23132  
23133})();
23134
23135</script>
23135
23136    
23136<script data-inlined="product-reviews.js">
23137/**
23138 * Product Reviews - Frontend JavaScript
23139 * Handles review display, submission, and helpful voting
23140 */
23141
23142const ProductReviews = (function() {
23143  'use strict';
23144
23145  let currentPage = 1;
23146  let currentSort = 'helpful';
23147  let productId = null;
23148  let isLoading = false;
23149
23150  /**
23151   * Public API: Initialize reviews for a product
23152   */
23153  function init(pid) {
23154    productId = pid;
23155    
23156    if (!productId) {
23157      console.warn('Product ID not provided to ProductReviews.init()');
23158      return;
23159    }
23160
23161    initializeReviewTab();
23162    loadReviews();
23163  }
23164
23165  /**
23166   * Initialize review tab functionality
23167   */
23168  function initializeReviewTab() {
23169    // Tab switching
23170    const tabButtons = document.querySelectorAll('.tab-btn');
23171    tabButtons.forEach(btn => {
23172      btn.addEventListener('click', function() {
23173        const tabName = this.dataset.tab;
23174        switchTab(tabName);
23175      });
23176    });
23177
23178    // Sort dropdown
23179    const sortSelect = document.getElementById('review-sort');
23180    if (sortSelect) {
23181      sortSelect.addEventListener('change', function() {
23182        currentSort = this.value;
23183        currentPage = 1;
23184        loadReviews();
23185      });
23186    }
23187
23188    // Load more button
23189    const loadMoreBtn = document.getElementById('load-more-reviews');
23190    if (loadMoreBtn) {
23191      loadMoreBtn.addEventListener('click', function() {
23192        currentPage++;
23193        loadReviews(true); // Append mode
23194      });
23195    }
23196
23197    // Write review button
23198    const writeReviewBtn = document.getElementById('write-review-btn');
23199    if (writeReviewBtn) {
23200      writeReviewBtn.addEventListener('click', showReviewModal);
23201    }
23202  }
23203
23204  /**
23205   * Switch between product tabs
23206   */
23207  function switchTab(tabName) {
23208    // Update button states
23209    document.querySelectorAll('.tab-btn').forEach(btn => {
23210      btn.classList.toggle('active', btn.dataset.tab === tabName);
23211    });
23212
23213    // Update content visibility
23214    document.querySelectorAll('.tab-content').forEach(content => {
23215      content.style.display = content.id === `${tabName}-tab` ? 'block' : 'none';
23216    });
23217  }
23218
23219  /**
23220   * Load reviews from API
23221   */
23222  async function loadReviews(append = false) {
23223    if (isLoading) return;
23224    isLoading = true;
23225
23226    const container = document.getElementById('reviews-list');
23227    const loadMoreBtn = document.getElementById('load-more-reviews');
23228
23229    // Check if review section exists (product might not have reviews section)
23230    if (!container) {
23231      console.log('Review section not found - product may not have reviews yet');
23232      isLoading = false;
23233      return;
23234    }
23235
23236    try {
23237      if (!append) {
23238        container.innerHTML = '<div class="review-loading">Memuat ulasan...</div>';
23239      } else {
23240        loadMoreBtn.disabled = true;
23241        loadMoreBtn.textContent = 'Memuat...';
23242      }
23243
23244      const response = await fetch(`/api/reviews/list.php?product_id=${productId}&sort=${currentSort}&page=${currentPage}&limit=10`);
23245      const data = await response.json();
23246
23247      if (!data.success) {
23248        throw new Error(data.message || 'Gagal memuat ulasan');
23249      }
23250
23251      // Update statistics (only on initial load)
23252      if (!append && data.stats) {
23253        updateReviewStats(data.stats);
23254      }
23255
23256      // Render reviews
23257      if (!append) {
23258        container.innerHTML = '';
23259      }
23260
23261      const reviews = data.reviews || [];
23262      
23263      if (reviews.length === 0 && !append) {
23264        container.innerHTML = '<div class="no-reviews">Belum ada ulasan untuk produk ini. Jadilah yang pertama!</div>';
23265      } else {
23266        reviews.forEach(review => {
23267          container.appendChild(createReviewElement(review));
23268        });
23269      }
23270
23271      // Update load more button
23272      if (loadMoreBtn) {
23273        if (data.pagination.has_more) {
23274          loadMoreBtn.style.display = 'block';
23275          loadMoreBtn.disabled = false;
23276          loadMoreBtn.textContent = 'Muat Lebih Banyak';
23277        } else {
23278          loadMoreBtn.style.display = 'none';
23279        }
23280      }
23281
23282    } catch (error) {
23283      console.error('Load reviews error:', error);
23284      container.innerHTML = '<div class="review-error">Gagal memuat ulasan. Silakan coba lagi.</div>';
23285    } finally {
23286      isLoading = false;
23287    }
23288  }
23289
23290  /**
23291   * Update review statistics display
23292   */
23293  function updateReviewStats(stats) {
23294    const avgRatingEl = document.getElementById('avg-rating');
23295    const totalReviewsEl = document.getElementById('total-reviews');
23296    const ratingBarsContainer = document.getElementById('rating-bars');
23297
23298    if (avgRatingEl) {
23299      avgRatingEl.textContent = stats.average_rating.toFixed(1);
23300    }
23301
23302    if (totalReviewsEl) {
23303      totalReviewsEl.textContent = `${stats.total_reviews} ulasan`;
23304    }
23305
23306    // Update rating breakdown bars
23307    if (ratingBarsContainer) {
23308      ratingBarsContainer.innerHTML = '';
23309      stats.rating_breakdown.forEach(item => {
23310        const bar = document.createElement('div');
23311        bar.className = 'rating-bar';
23312        bar.innerHTML = `
23313          <span class="rating-label">${item.rating}★</span>
23314          <div class="bar-container">
23315            <div class="bar-fill" style="width: ${item.percentage}%"></div>
23316          </div>
23317          <span class="rating-count">${item.count}</span>
23318        `;
23319        ratingBarsContainer.appendChild(bar);
23320      });
23321    }
23322
23323    // Update tab count
23324    const reviewTab = document.querySelector('[data-tab="reviews"]');
23325    if (reviewTab && stats.total_reviews > 0) {
23326      reviewTab.textContent = `Ulasan (${stats.total_reviews})`;
23327    }
23328  }
23329
23330  /**
23331   * Create review element
23332   */
23333  function createReviewElement(review) {
23334    const div = document.createElement('div');
23335    div.className = 'review-item';
23336    div.dataset.reviewId = review.ID;
23337
23338    // Build stars
23339    const stars = '★'.repeat(review.RATING) + '☆'.repeat(5 - review.RATING);
23340
23341    // Build images if available
23342    let imagesHtml = '';
23343    if (review.images && review.images.length > 0) {
23344      imagesHtml = '<div class="review-images">';
23345      review.images.forEach(img => {
23346        imagesHtml += `<img src="${escapeHtml(img)}" alt="Review image" class="review-img" loading="lazy">`;
23347      });
23348      imagesHtml += '</div>';
23349    }
23350
23351    // Verified purchase badge
23352    const verifiedBadge = review.IS_VERIFIED_PURCHASE === 'Y' 
23353      ? '<span class="verified-badge">✓ Pembelian Terverifikasi</span>' 
23354      : '';
23355    
23356    // Use DISPLAY_NAME if available (for anonymous reviews), otherwise use FULL_NAME
23357    const displayName = review.DISPLAY_NAME || review.FULL_NAME || 'Anonymous';
23358    const anonymousIndicator = review.IS_ANONYMOUS === 'Y' 
23359      ? '<span class="anonymous-badge" title="Nama pengguna disamark
23359an untuk privasi">🔒</span>' 
23360      : '';
23361
23362    div.innerHTML = `
23363      <div class="review-header">
23364        <div class="review-author">
23365          <strong>${escapeHtml(displayName)}</strong>
23366          ${anonymousIndicator}
23367          ${verifiedBadge}
23368        </div>
23369        <div class="review-rating">
23370          <span class="stars">${stars}</span>
23371        </div>
23372      </div>
23373      <div class="review-date">${review.created_ago}</div>
23374      <div class="review-text">${escapeHtml(review.REVIEW_TEXT).replace(/\n/g, '<br>')}</div>
23375      ${imagesHtml}
23376      <div class="review-actions">
23377        <button class="btn-helpful" data-review-id="${review.ID}">
23378          <span class="helpful-icon">👍</span>
23379          <span class="helpful-text">Membantu (${review.HELPFUL_COUNT || 0})</span>
23380        </button>
23381      </div>
23382    `;
23383
23384    // Add helpful button listener
23385    const helpfulBtn = div.querySelector('.btn-helpful');
23386    helpfulBtn.addEventListener('click', () => markAsHelpful(review.ID));
23387
23388    return div;
23389  }
23390
23391  /**
23392   * Get CSRF token from meta tag
23393   */
23394  function getCsrfToken() {
23395    const metaTag = document.querySelector('meta[name="csrf-token"]');
23396    return metaTag ? metaTag.getAttribute('content') : '';
23397  }
23398
23399  /**
23400   * Mark review as helpful
23401   */
23402  async function markAsHelpful(reviewId) {
23403    // Check if logged in
23404    const isLoggedIn = document.body.classList.contains('logged-in') || 
23405                      document.querySelector('meta[name="user-logged-in"]')?.content === 'true';
23406
23407    if (!isLoggedIn) {
23408      showModal({
23409        title: 'Login Diperlukan',
23410        message: 'Silakan login terlebih dahulu untuk memberikan vote pada ulasan ini.',
23411        icon: 'info',
23412        buttons: [
23413          { text: 'Batal', className: 'modal-btn-secondary' },
23414          { text: 'Login', className: 'modal-btn-primary', callback: () => window.location.href = '/login' }
23415        ]
23416      });
23417      return;
23418    }
23419
23420    // Get current button element
23421    const reviewItem = document.querySelector(`[data-review-id="${reviewId}"]`);
23422    const helpfulBtn = reviewItem?.querySelector('.btn-helpful');
23423    
23424    if (!helpfulBtn) return;
23425    
23426    // Disable button during request
23427    helpfulBtn.disabled = true;
23428    const originalText = helpfulBtn.querySelector('.helpful-text').textContent;
23429
23430    try {
23431      const response = await fetch('/api/reviews/helpful.php', {
23432        method: 'POST',
23433        headers: { 
23434          'Content-Type': 'application/json',
23435          'X-CSRF-Token': getCsrfToken()
23436        },
23437        credentials: 'same-origin',
23438        body: JSON.stringify({ 
23439          csrf_token: getCsrfToken(),
23440          review_id: reviewId 
23441        })
23442      });
23443
23444      const data = await response.json();
23445
23446      if (data.success) {
23447        // Update button count immediately based on action
23448        const currentCount = parseInt(originalText.match(/\d+/)?.[0] || 0);
23449        const newCount = data.action === 'added' ? currentCount + 1 : currentCount - 1;
23450        helpfulBtn.querySelector('.helpful-text').textContent = `Membantu (${newCount})`;
23451        
23452        // Show success message
23453        showToast(data.message, 'success');
23454        
23455        // Re-enable button
23456        helpfulBtn.disabled = false;
23457      } else {
23458        showToast(data.message, 'error');
23459        helpfulBtn.disabled = false;
23460      }
23461
23462    } catch (error) {
23463      console.error('Mark helpful error:', error);
23464      showToast('Gagal memberikan vote. Silakan coba lagi.', 'error');
23465      helpfulBtn.disabled = false;
23466    }
23467  }
23468
23469  /**
23470   * Show review submission modal (placeholder)
23471   */
23472  function showReviewModal() {
23473    // TODO: Implement review modal
23474    // For now, redirect to account page
23475    window.location.href = '/account/orders';
23476  }
23477
23478  /**
23479   * Show toast notification
23480   */
23481  function showToast(message, type = 'info') {
23482    const toast = document.createElement('div');
23483    toast.className = `review-toast toast-${type}`;
23484    toast.textContent = message;
23485    
23486    document.body.appendChild(toast);
23487    
23488    setTimeout(() => {
23489      toast.classList.add('show');
23490    }, 100);
23491    
23492    setTimeout(() => {
23493      toast.classList.remove('show');
23494      setTimeout(() => toast.remove(), 300);
23495    }, 3000);
23496  }
23497
23498  /**
23499   * Show modal dialog
23500   * @param {Object} options - Modal options
23501   * @param {string} options.title - Modal title
23502   * @param {string} options.message - Modal message
23503   * @param {string} options.icon - Icon type (info, success, warning, danger)
23504   * @param {Array} options.buttons - Array of button objects {text, className, callback}
23505   */
23506  function showModal(options) {
23507    const {
23508      title = 'Konfirmasi',
23509      message = '',
23510      icon = 'info',
23511      buttons = [{ text: 'OK', className: 'modal-btn-primary' }]
23512    } = options;
23513
23514    // Icon emoji mapping
23515    const iconEmojis = {
23516      info: 'ℹ️',
23517      success: '✓',
23518      warning: '⚠️',
23519      danger: '✕'
23520    };
23521
23522    // Create modal HTML
23523    const modalHTML = `
23524      <div class="modal-overlay" id="helpful-vote-modal" style="display: flex !important; align-items: center !important; justify-content: center !important;">
23525        <div class="modal-dialog">
23526          <div class="modal-header">
23527            <h3 class="modal-title">
23528              <span class="modal-icon ${icon}">${iconEmojis[icon] || iconEmojis.info}</span>
23529              ${escapeHtml(title)}
23530            </h3>
23531          </div>
23532          <div class="modal-body">
23533            <p class="modal-message">${escapeHtml(message)}</p>
23534            <div class="modal-actions">
23535              ${buttons.map((btn, index) => 
23536                `<button class="modal-btn ${btn.className || 'modal-btn-secondary'}" data-index="${index}">${escapeHtml(btn.text)}</button>`
23537              ).join('')}
23538            </div>
23539          </div>
23540        </div>
23541      </div>
23542    `;
23543
23544    // Append to body
23545    const tempDiv = document.createElement('div');
23546    tempDiv.innerHTML = modalHTML;
23547    const modal = tempDiv.firstElementChild;
23548    document.body.appendChild(modal);
23549
23550    // Add event listeners to buttons
23551    buttons.forEach((btn, index) => {
23552      const btnElement = modal.querySelector(`[data-index="${index}"]`);
23553      btnElement.addEventListener('click', () => {
23554        closeModal(modal);
23555        if (btn.callback && typeof btn.callback === 'function') {
23556          btn.callback();
23557        }
23558      });
23559    });
23560
23561    // Close on overlay click
23562    modal.addEventListener('click', (e) => {
23563      if (e.target === modal) {
23564        closeModal(modal);
23565      }
23566    });
23567
23568    // Show modal with animation
23569    setTimeout(() => modal.classList.add('show'), 10);
23570  }
23571
23572  /**
23573   * Close modal dialog
23574   */
23575  function closeModal(modal) {
23576    modal.classList.remove('show');
23577    setTimeout(() => modal.remove(), 200);
23578  }
23579
23580  /**
23581   * Escape HTML for XSS protection
23582   */
23583  function escapeHtml(text) {
23584    const div = document.createElement('div');
23585    div.textContent = text;
23586    return div.innerHTML;
23587  }
23588
23589  /**
23590   * Get product ID from URL
23591   */
23592  function getProductIdFromUrl() {
23593    const match = window.location.pathname.match(/\/product\/(\d+)/);
23594    return match ? match[1] : null;
23595  }
23596
23597  // Public API
23598  return {
23599    init: init
23600  };
23601
23602})();
23603
23604</script>
23604
23605    
23605<script data-inlined="review-submission.js">
23606/**
23607 * Review Submission Modal & Handler
23608 * Handles review form display, validation, and submission
23609 */
23610
23611(function() {
23612    'use strict';
23613
23614    let currentProductId = null;
23615    let currentOrderId = null;
23616    let currentSoRefId = null;
23617    let selectedRating = 0;
23618    let uploadedImages = [];
23619
23620    // Get CSRF token from meta tag
23621    function getCsrfToken() {
23622        const metaTag = document.querySelector('meta[name="csrf-token"]');
23623        return metaTag ? metaTag.getAttribute('content') : '';
23624    }
23625
23626    // Initialize
23627    function init() {
23628        createReviewModal();
23629        attachEventListeners();
23630        loadReviewableOrders();
23631    }
23632
23633    // Create review modal HTML
23634    function createReviewModal() {
23635        const modalHTML = `
23636            <div class="modal-overlay" id="review-modal-overlay" style="display:none;">
23637                <div class="modal-content review-modal">
23638                    <div class="modal-header">
23639                        <h3 class="modal-title">Tulis Ulasan</h3>
23640                        <button type="button" class="modal-close" id="review-modal-close" aria-label="Tutup">
23641                            <svg width="20" height="20" viewBox="0 0 24 24">
23642                                <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"/>
23643                            </svg>
23644                        </button>
23645                    </div>
23646                    
23647                    <div class="modal-body">
23648                        <form id="review-form" class="review-form">
23649                            <!-- Product Info -->
23650                            <div class="review-product-info" id="review-product-info">
23651                                <img id="review-product-image" src="" alt="" class="review-product-thumb">
23652                                <div class="review-product-details">
23653                                    <h4 id="review-product-name"></h4>
23654                                    <p id="review-product-sku"></p>
23655                                </div>
23656                            </div>
23657
23658                            <!-- Rating -->
23659                            <div class="form-group">
23660                                <label class="form-label">Rating <span style="color:#ef4444;">*</span></label>
23661                                <div class="star-rating-input" id="star-rating-input">
23662                                    <button type="button" class="star-btn" data-rating="1">★</button>
23663                                    <button type="button" class="star-btn" data-rating="2">★</button>
23664                                    <button type="button" class="star-btn" data-rating="3">★</button>
23665                                    <button type="button" class="star-btn" data-rating="4">★</button>
23666                                    <button type="button" class="star-btn" data-rating="5">★</button>
23667                                </div>
23668                                <p class="rating-label" id="rating-label">Pilih rating Anda</p>
23669                            </div>
23670
23671                            <!-- Review Text -->
23672                            <div class="form-group">
23673                                <label class="form-label" for="review-text">Ulasan Anda <span style="color:#ef4444;">*</span></label>
23674                                <textarea 
23675                                    id="review-text" 
23676                                    name="review_text"
23677                                    class="form-control" 
23678                                    rows="6" 
23679                                    placeholder="Ceritakan pengalaman Anda dengan produk ini..."
23680                                    minlength="20"
23681                                    maxlength="5000"
23682                                    required></textarea>
23683                                <p class="form-hint">
23684                                    <span id="review-char-count">0</span>/5000 karakter
23685                                    <span style="color:#059669;font-weight:500;">
23686                                        💰 +1,000 point untuk ulasan ≥ 100 karakter
23687                                    </span>
23688                                </p>
23689                            </div>
23690
23691                            <!-- Image Upload -->
23692                            <div class="form-group">
23693                                <label class="form-label">Upload Foto (Opsional)</label>
23694                                <div class="image-upload-container">
23695                                    <input type="file" 
23696                                           id="review-images" 
23697                                           name="images[]" 
23698                                           accept="image/jpeg,image/jpg,image/png,image/webp"
23699                                           multiple
23700                                           style="display:none;">
23701                                    <button type="button" class="btn-upload-trigger" id="upload-trigger">
23702                                        <svg width="24" height="24" viewBox="0 0 24 24">
23703                                            <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"/>
23704                                        </svg>
23705                                        <span>Pilih Foto</span>
23706                                    </button>
23707                                    <p class="form-hint">
23708                                        Max 5 foto, format JPG/PNG/WebP, max 2MB per foto
23709                                        <span style="color:#059669;font-weight:500;">
23710                                            💰 +2,000 point untuk ulasan dengan foto
23711                                        </span>
23712                                    </p>
23713                                </div>
23714                                <div class="image-preview-grid" id="image-preview-grid"></div>
23715                            </div>
23716
23717                            <!-- Point Reward Info -->
23718                            <div class="point-reward-info">
23719                                <div class="pri-header">
23720                                    <span>🎁</span>
23721                                    <span>Dapatkan Point Reward</span>
23722                                </div>
23723                                <ul class="pri-list">
23724                                    <li>✅ Ulasan dasar: <strong>5,000 point</strong></li>
23725                                    <li id="pri-long-text" style="opacity:0.5;">📝 Ulasan panjang (≥100 kata): <strong>+1,000 point</strong></li>
23726                                    <li id="pri-images" style="opacity:0.5;">📸 Dengan foto: <strong>+2,000 point</strong></li>
23727                                </ul>
23728                                <div class="pri-total">
23729                                    Total Reward: <strong id="pri-total-points">5,000 point</strong>
23730                                </div>
23731                            </div>
23732
23733                            <!-- Privacy Option -->
23734                            <div class="form-group" style="margin-top:20px;padding:15px;background:#f8fafc;border-radius:8px;">
23735                                <label class="checkbox-label" style="display:flex;align-items:center;cursor:pointer;margin:0;">
23736                                    <input type="checkbox" 
23737                                           id="review-anonymous" 
23738                                           name="is_anonymous"
23739                                           style="margin-right:8px;width:18px;height:18px;cursor:pointer;">
23740                                    <span style="font-size:14px;">
23741                                        <strong>🔒 Sembunyikan nama saya</strong>
23742                                        <span style="color:#64748b;display:block;font-size:13px;margin-top:4px;" id="review-anonymous-preview">
23743                                            Nama Anda akan ditampilkan de
23743ngan disamarkan untuk menjaga privasi
23744                                        </span>
23745                                    </span>
23746                                </label>
23747                            </div>
23748
23749                            <!-- Error Alert -->
23750                            <div class="form-error" id="review-form-error" style="display:none;"></div>
23751
23752                            <!-- Submit Buttons -->
23753                            <div class="modal-footer">
23754                                <button type="button" class="btn btn-secondary" id="review-cancel">Batal</button>
23755                                <button type="submit" class="btn btn-primary" id="review-submit">
23756                                    Kirim Ulasan
23757                                </button>
23758                            </div>
23759                        </form>
23760                    </div>
23761                </div>
23762            </div>
23763        `;
23764
23765        document.body.insertAdjacentHTML('beforeend', modalHTML);
23766    }
23767
23768    // Attach event listeners
23769    function attachEventListeners() {
23770        // Modal triggers - event delegation
23771        document.addEventListener('click', function(e) {
23772            const btn = e.target.closest('.btn-write-review');
23773            if (btn) {
23774                e.preventDefault();
23775                openReviewModal(
23776                    btn.dataset.productId,
23777                    btn.dataset.orderId,
23778                    btn.dataset.productName,
23779                    btn.dataset.productImage,
23780                    btn.dataset.productSku,
23781                    btn.dataset.soRefId || null
23782                );
23783            }
23784        });
23785
23786        // Close modal
23787        setTimeout(() => {
23788            const closeBtn = document.getElementById('review-modal-close');
23789            const cancelBtn = document.getElementById('review-cancel');
23790            const overlay = document.getElementById('review-modal-overlay');
23791
23792            if (closeBtn) {
23793                closeBtn.addEventListener('click', closeReviewModal);
23794            }
23795            if (cancelBtn) {
23796                cancelBtn.addEventListener('click', closeReviewModal);
23797            }
23798            if (overlay) {
23799                overlay.addEventListener('click', function(e) {
23800                    if (e.target === overlay) closeReviewModal();
23801                });
23802            }
23803        }, 100);
23804
23805        // Star rating
23806        setTimeout(() => {
23807            const starButtons = document.querySelectorAll('.star-btn');
23808            starButtons.forEach(btn => {
23809                btn.addEventListener('click', function(e) {
23810                    e.preventDefault();
23811                    const rating = parseInt(this.dataset.rating);
23812                    setRating(rating);
23813                });
23814            });
23815        }, 100);
23816
23817        // Character count
23818        setTimeout(() => {
23819            const textarea = document.getElementById('review-text');
23820            if (textarea) {
23821                textarea.addEventListener('input', function() {
23822                    updateCharCount();
23823                    calculatePotentialPoints();
23824                });
23825            }
23826        }, 100);
23827
23828        // Image upload
23829        setTimeout(() => {
23830            const uploadTrigger = document.getElementById('upload-trigger');
23831            const fileInput = document.getElementById('review-images');
23832            
23833            if (uploadTrigger && fileInput) {
23834                uploadTrigger.addEventListener('click', () => fileInput.click());
23835                fileInput.addEventListener('change', handleImageSelection);
23836            }
23837        }, 100);
23838
23839        // Form submit
23840        setTimeout(() => {
23841            const form = document.getElementById('review-form');
23842            if (form) {
23843                form.addEventListener('submit', handleSubmit);
23844            }
23845        }, 100);
23846    }
23847
23848    // Open review modal
23849    function openReviewModal(productId, orderId, productName, productImage, productSku, soRefId = null) {
23850        const overlay = document.getElementById('review-modal-overlay');
23851        if (!overlay) {
23852            console.error('Modal overlay not found!');
23853            return;
23854        }
23855        
23856        currentProductId = productId;
23857        currentOrderId = orderId;
23858        currentSoRefId = soRefId;
23859        selectedRating = 0;
23860        uploadedImages = [];
23861
23862        // Set product info
23863        const nameEl = document.getElementById('review-product-name');
23864        const imageEl = document.getElementById('review-product-image');
23865        const skuEl = document.getElementById('review-product-sku');
23866        
23867        if (nameEl) nameEl.textContent = productName;
23868        if (imageEl) imageEl.src = productImage || '/assets//img/no-image.png';
23869        if (skuEl) skuEl.textContent = 'SKU: ' + productSku;
23870
23871        // Update privacy preview with actual user name
23872        updatePrivacyPreview();
23873
23874        // Reset form
23875        const form = document.getElementById('review-form');
23876        const previewGrid = document.getElementById('image-preview-grid');
23877        const errorEl = document.getElementById('review-form-error');
23878        
23879        if (form) form.reset();
23880        if (previewGrid) previewGrid.innerHTML = '';
23881        if (errorEl) errorEl.style.display = 'none';
23882        
23883        setRating(0);
23884        updateCharCount();
23885        calculatePotentialPoints();
23886
23887        // Show modal
23888        overlay.style.display = 'flex';
23889        overlay.classList.add('show');
23890        document.body.style.overflow = 'hidden';
23891    }
23892
23893    // Close review modal
23894    function closeReviewModal() {
23895        const overlay = document.getElementById('review-modal-overlay');
23896        if (overlay) {
23897            overlay.classList.remove('show');
23898            setTimeout(() => {
23899                overlay.style.display = 'none';
23900            }, 200); // Wait for fade animation
23901        }
23902        document.body.style.overflow = '';
23903    }
23904
23905    // Set star rating
23906    function setRating(rating) {
23907        selectedRating = rating;
23908        const stars = document.querySelectorAll('.star-btn');
23909        const labels = ['', 'Sangat Buruk', 'Buruk', 'Cukup', 'Bagus', 'Sangat Bagus'];
23910        
23911        stars.forEach((star, index) => {
23912            if (index < rating) {
23913                star.classList.add('active');
23914            } else {
23915                star.classList.remove('active');
23916            }
23917        });
23918
23919        document.getElementById('rating-label').textContent = labels[rating] || 'Pilih rating Anda';
23920    }
23921
23922    // Update character count
23923    function updateCharCount() {
23924        const textarea = document.getElementById('review-text');
23925        const count = textarea.value.length;
23926        document.getElementById('review-char-count').textContent = count;
23927    }
23928
23929    // Calculate potential points
23930    function calculatePotentialPoints() {
23931        const textarea = document.getElementById('review-text');
23932        if (!textarea) return;
23933        
23934        const charCount = textarea.value.trim().length;
23935        const hasImages = uploadedImages.length > 0;
23936
23937        let totalPoints = 5000;
23938        let longTextActive = charCount >= 100;
23939        let imagesActive = hasImages;
23940
23941        if (longTextActive) totalPoints += 1000;
23942        if (imagesActive) totalPoints += 2000;
23943
23944        // Update UI
23945        const longTextEl = document.getElementById('pri-long-text');
23946        const imagesEl = document.getElementById('pri-images');
23947        const totalEl = document.getElementById('pri-total-points');
23948        
23949        if (longTextEl) longTextEl.style.opacity = longTextActive ? '1' : '0.5';
23950        if (imagesEl) imagesEl.style.opacity = imagesActive ? '1' : '0.5';
23951        if (totalEl) totalEl.textContent = totalPoints.toLocaleString('id-ID') + ' point';
23952    }
23953
23954    // Handle image selection
23955    function handleImageSelection(e) {
23956        const files = Array.from(e.target.files);
23957        
23958        if (files.length > 5) {
23959            showError('Maksimal 5 foto yang dapat diupload');
23960            e.target.value = ''; // Reset input
23961            return;
23962        }
23963
23964        uploadedImages = [];
23965        const previewGrid = document.getElementById('image-preview-grid');
23966        previewGrid.innerHTML = '';
23967
23968        files.forEach((file, index) => {
23969            // Validate file type
23970            if (!file.type.match(/image\/(jpeg|jpg|png|webp)/)) {
23971                showError('Format file harus JPG, PNG, atau WebP');
23972                return;
23973            }
23974
23975            // Validate file size (2MB)
23976            if (file.size > 2 * 1024 * 1024) {
23977                showError('Ukuran file maksimal 2MB');
23978                return;
23979            }
23980
23981            uploadedImages.push(file);
23982
23983            // Create preview
23984            const reader = new FileReader();
23985            reader.onload = function(e) {
23986                const previewHTML = `
23987                    <div class="image-preview-item">
23988                        <img src="${e.target.result}" alt="Preview ${index + 1}">
23989                        <button type="button" class="remove-image" data-index="${index}" aria-label="Hapus">
23990                            <svg width="16" height="16" viewBox="0 0 24 24">
23991                                <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"/>
23992                            </svg>
23993                        </button>
23994                    </div>
23995                `;
23996                previewGrid.insertAdjacentHTML('beforeend', previewHTML);
23997            };
23998            reader.readAsDataURL(file);
23999        });
24000
24001        calculatePotentialPoints();
24002
24003        // Attach remove handlers
24004        setTimeout(() => {
24005            document.querySelectorAll('.remove-image').forEach(btn => {
24006                btn.addEventListener('click', function() {
24007                    const index = parseInt(this.dataset.index);
24008                    removeImage(index);
24009                });
24010            });
24011        }, 100);
24012    }
24013
24014    // Remove image
24015    function removeImage(index) {
24016        uploadedImages.splice(index, 1);
24017        
24018        const previewGrid = document.getElementById('image-preview-grid');
24019        previewGrid.innerHTML = '';
24020
24021        uploadedImages.forEach((file, idx) => {
24022            const reader = new FileReader();
24023            reader.onload = function(e) {
24024                const previewHTML = `
24025                    <div class="image-preview-item">
24026                        <img src="${e.target.result}" alt="Preview ${idx + 1}">
24027                        <button type="button" class="remove-image" data-index="${idx}" aria-label="Hapus">
24028                            <svg width="16" height="16" viewBox="0 0 24 24">
24029                                <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"/>
24030                            </svg>
24031                        </button>
24032                    </div>
24033                `;
24034                previewGrid.insertAdjacentHTML('beforeend', previewHTML);
24035            };
24036            reader.readAsDataURL(file);
24037        });
24038
24039        calculatePotentialPoints();
24040
24041        setTimeout(() => {
24042            document.querySelectorAll('.remove-image').forEach(btn => {
24043                btn.addEventListener('click', function() {
24044                    const idx = parseInt(this.dataset.index);
24045                    removeImage(idx);
24046                });
24047            });
24048        }, 100);
24049    }
24050
24051    // Handle form submission
24052    async function handleSubmit(e) {
24053        e.preventDefault();
24054
24055        // Validate
24056        if (selectedRating === 0) {
24057            showError('Silakan pilih rating terlebih dahulu');
24058            return;
24059        }
24060
24061        const reviewText = document.getElementById('review-text').value.trim();
24062        if (reviewText.length < 20) {
24063            showError('Ulasan minimal 20 karakter');
24064            return;
24065        }
24066
24067        const submitBtn = document.getElementById('review-submit');
24068        submitBtn.disabled = true;
24069        
24070        try {
24071            // Step 1: Upload images if any
24072            let imageUrls = [];
24073            if (uploadedImages.length > 0) {
24074                submitBtn.textContent = 'Mengupload foto...';
24075                imageUrls = await uploadImages();
24076                if (!imageUrls) {
24077                    // Upload failed
24078                    submitBtn.disabled = false;
24079                    submitBtn.textContent = 'Kirim Ulasan';
24080                    return;
24081                }
24082            }
24083
24084            // Step 2: Submit review with image URLs
24085            submitBtn.textContent = 'Mengirim ulasan...';
24086            
24087            const payload = {
24088                csrf_token: getCsrfToken(),
24089                product_id: currentProductId,
24090                order_id: currentOrderId || 0,
24091                so_ref_id: currentSoRefId || undefined,
24092                rating: selectedRating,
24093                review_text: reviewText,
24094                is_anonymous: document.getElementById('review-anonymous').checked,
24095                images: imageUrls
24096            };
24097
24098            const response = await fetch('/api/reviews/submit.php', {
24099                method: 'POST',
24100                headers: {
24101                    'Content-Type': 'application/json',
24102                    'X-CSRF-Token': getCsrfToken()
24103                },
24104                body: JSON.stringify(payload)
24105            });
24106
24107            const result = await response.json();
24108
24109            if (result.success) {
24110                showSuccess(result.message || 'Ulasan berhasil dikirim!');
24111                closeReviewModal();
24112                
24113                // Refresh reviewable orders if on account page
24114                if (typeof loadReviewableOrders === 'function') {
24115                    setTimeout(loadReviewableOrders, 500);
24116                }
24117            } else {
24118                showError(result.message || 'Gagal mengirim ulasan');
24119            }
24120        } catch (error) {
24121            console.error('Submit error:', error);
24122            showError('Terjadi kesalahan saat mengirim ulasan');
24123        } finally {
24124            submitBtn.disabled = false;
24125            submitBtn.textContent = 'Kirim Ulasan';
24126        }
24127    }
24128
24129    /**
24130     * Upload images to server
24131     * @returns {Promise<Array|null>} Array of image URLs or null if failed
24132     */
24133    async function uploadImages() {
24134        if (uploadedImages.length === 0) {
24135            return [];
24136        }
24137
24138        const formData = new FormData();
24139        uploadedImages.forEach(file => {
24140            formData.append('images[]', file);
24141        });
24142
24143        try {
24144            const response = await fetch('/api/reviews/upload_images.php', {
24145                method: 'POST',
24146                headers: {
24147                    'X-CSRF-Token': getCsrfToken()
24148                },
24149                body: formData
24150            });
24151
24152            // Check if response is JSON
24153            const contentType = response.headers.get('content-type');
24154            if (!contentType || !contentType.includes('application/json')) {
24155                const text = await response.text();
24156                console.error('Non-JSON response:', text);
24157                showError('Server error: Response bukan JSON. Cek browser console untuk detail.');
24158                return null;
24159            }
24160
24161            const result = await response.json();
24162
24163            if (result.success && result.images) {
24164                // Extract URLs from uploaded images
24165                return result.images.map(img => img.url);
24166            } else {
24167                showError(result.message || 'Gagal mengupload foto');
24168                return null;
24169            }
24170        } catch (error) {
24171            console.error('Upload images error:', error);
24172            showError('Terjadi kesalahan saat mengupload foto. Cek console untuk detail.');
24173            return null;
24174        }
24175    }
24176
24177    // Load reviewable orders (for account page)
24178    async function loadReviewableOrders() {
24179        // Only run if on account/profile page
24180        const container = document.getElementById('reviewable-orders-list');
24181        const loading = document.getElementById('reviewable-orders-loading');
24182        const empty = document.getElementById('reviewable-orders-empty');
24183        
24184        if (!container) {
24185            return;
24186        }
24187
24188        // Show loading
24189        if (loading) loading.style.display = 'block';
24190        if (empty) empty.style.display = 'none';
24191        container.innerHTML = '';
24192
24193        try {
24194            const response = await fetch('/api/reviews/reviewable.php');
24195            const result = await response.json();
24196
24197            if (loading) loading.style.display = 'none';
24198
24199            if (result.success && result.data.length > 0) {
24200                renderReviewableOrders(result.data);
24201                if (empty) empty.style.display = 'none';
24202            } else {
24203                if (empty) empty.style.display = 'block';
24204            }
24205        } catch (error) {
24206            console.error('Load reviewable orders error:', error);
24207            if (loading) loading.style.display = 'none';
24208            if (empty) {
24209                empty.innerHTML = '<p>Gagal memuat data. Silakan refresh halaman.</p>';
24210                empty.style.display = 'block';
24211            }
24212        }
24213    }
24214
24215    // Render reviewable orders
24216    function renderReviewableOrders(orders) {
24217        const container = document.getElementById('reviewable-orders-list');
24218        if (!container) return;
24219
24220        let html = '';
24221        
24222        orders.forEach(order => {
24223            html += `
24224                <div class="reviewable-order-item">
24225                    <img src="${escapeHtml(order.product_image || '/assets//img/no-image.png')}" alt="${escapeHtml(order.product_name)}" class="roi-image">
24226                    <div class="roi-info">
24227                        <h4>${escapeHtml(order.product_name)}</h4>
24228                        <p class="roi-meta">Order: ${escapeHtml(order.order_number)}</p>
24229                        <p class="roi-meta">Dibeli: ${escapeHtml(order.order_date)}</p>
24230                    </div>
24231                    <button type="button" 
24232                            class="btn btn-primary btn-write-review"
24233                            data-product-id="${order.product_id}"
24234                            data-order-id="${order.order_id || ''}"
24235                            data-so-ref-id="${escapeHtml(order.so_ref_id || '')}"
24236                            data-product-name="${escapeHtml(order.product_name)}"
24237                            data-product-image="${escapeHtml(order.product_image || '')}"
24238                            data-product-sku="${escapeHtml(order.product_sku || '')}">
24239                        ✍️ Tulis Ulasan
24240                    </button>
24241                </div>
24242            `;
24243        });
24244        
24245        container.innerHTML = html;
24246    }
24247
24248    // Escape HTML for XSS protection
24249    function escapeHtml(text) {
24250        const div = document.createElement('div');
24251        div.textContent = text;
24252        return div.innerHTML;
24253    }
24254
24255    // Show error
24256    function showError(message) {
24257        const errorEl = document.getElementById('review-form-error');
24258        errorEl.textContent = message;
24259        errorEl.style.display = 'block';
24260        
24261        setTimeout(() => {
24262            errorEl.style.display = 'none';
24263        }, 5000);
24264    }
24265
24266    // Show success
24267    function showSuccess(message) {
24268        // Create toast notification
24269        const toast = document.createElement('div');
24270        toast.className = 'toast-notification success';
24271        toast.textContent = message;
24272        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;';
24273        
24274        document.body.appendChild(toast);
24275        
24276        setTimeout(() => {
24277            toast.style.opacity = '0';
24278            toast.style.transition = 'opacity 0.3s';
24279            setTimeout(() => toast.remove(), 300);
24280        }, 3000);
24281    }
24282
24283    // Mask user name for privacy preview (like PHP maskReviewerName)
24284    function maskUserName(fullName) {
24285        if (!fullName || fullName.trim() === '') {
24286            return 'Anonymous';
24287        }
24288        
24289        const words = fullName.trim().split(/\s+/);
24290        const maskedWords = words.map(word => {
24291            const len = word.length;
24292            if (len <= 1) return word;
24293            return word.charAt(0) + '*'.repeat(len - 1);
24294        });
24295        
24296        return maskedWords.join(' ');
24297    }
24298
24299    // Update privacy preview text with user's actual masked name
24300    function updatePrivacyPreview() {
24301        const previewEl = document.getElementById('review-anonymous-preview');
24302        if (!previewEl) return;
24303        
24304        const userName = window.userFullName || '';
24305        if (userName) {
24306            const maskedName = maskUserName(userName);
24307            previewEl.textContent = `Nama Anda akan ditampilkan sebagai "${maskedName}" untuk menjaga privasi`;
24308        } else {
24309            previewEl.textContent = 'Nama Anda akan ditampilkan de
24309ngan disamarkan untuk menjaga privasi';
24310        }
24311    }
24312
24313    // Initialize when DOM ready
24314    if (document.readyState === 'loading') {
24315        document.addEventListener('DOMContentLoaded', init);
24316    } else {
24317        init();
24318    }
24319
24320    // Export openReviewModal to global scope for use in other scripts
24321    window.openReviewModal = openReviewModal;
24322
24323})();
24324
24325</script>
24325
24326    <style>
24327.slot-top-fixed-footer{
24328      position: fixed;
24329    left: 0;
24330    right: 0;
24331    bottom: 0;
24332    z-index: 999;
24333    display: flex;
24334    align-items: center;
24335    justify-content: space-around;
24336    gap: 8px;
24337    padding: 11px 12px calc(11px + env(safe-area-inset-bottom));
24338    overflow: hidden;
24339    background: radial-gradient(circle at 50% 0%, #000000, rgb(0 0 0 / 92%) 58%, rgb(0 0 0 / 98%)), linear-gradient(180deg, rgba(11, 26, 51, 0.92), rgb(0 0 0 / 98%));
24340    backdrop-filter: blur(12px);
24341    border-top: 1px solid rgb(234 179 8);
24342    border-radius: 18px 18px 0 0;
24343    box-shadow: 0 -12px 30px #0B1A33, inset 0 1px 0 rgba(255, 255, 255, .05);
24344    font-family: "Orbitron", "Poppins", system-ui, sans-serif;
24345}
24346
24347.slot-top-fixed-footer::before{
24348  content:"";
24349  position:absolute;
24350  inset:0;
24351  pointer-events:none;
24352  background:
24353    repeating-linear-gradient(
24354      0deg,
24355      rgba(255,255,255,.015),
24356      rgba(255,255,255,.015) 1px,
24357      transparent 1px,
24358      transparent 6px
24359    );
24360  opacity:.85;
24361  mix-blend-mode:overlay;
24362}
24363
24364.slot-top-fixed-footer::after{
24365  content:"";
24366  position:absolute;
24367  top:0;
24368  left:-160%;
24369  width:60%;
24370  height:100%;
24371  background: linear-gradient(120deg, transparent, rgba(251, 191, 36, 0.25), transparent);
24372  transform: skewX(-25deg);
24373  animation: footerFinalShine 3.8s infinite;
24374  mix-blend-mode: screen;
24375  pointer-events:none;
24376}
24377
24378@keyframes footerFinalShine{
24379  0%{left:-160%;}
24380  68%{left:160%;}
24381  100%{left:160%;}
24382}
24383
24384.slot-top-fixed-footer a{
24385  flex:1;
24386  max-width:110px;
24387  display:flex;
24388  flex-direction:column;
24389  align-items:center;
24390  justify-content:center;
24391  gap:6px;
24392  padding:8px 6px;
24393  text-decoration:none;
24394  text-align:center;
24395  color:#D4AF37;
24396  font-size:12px;
24397  font-weight:800;
24398  letter-spacing:.25px;
24399  border-radius:14px;
24400  position:relative;
24401  z-index:1;
24402  transition:all .18s ease;
24403}
24404
24405.slot-top-fixed-footer a:hover{
24406  color:#ffffff;
24407  background:rgba(212,175,55,.1);
24408  filter:drop-shadow(0 0 12px rgba(212,175,55,.2));
24409  transform:translateY(-1px);
24410}
24411
24412.slot-top-fixed-footer a:active{
24413  transform:translateY(0) scale(.99);
24414}
24415
24416.slot-top-fixed-footer img,
24417.slot-top-fixed-footer .footer-nav-icon{
24418  width:26px;
24419  height:26px;
24420  transition:.18s ease;
24421  filter:drop-shadow(0 0 8px rgba(234,179,8,.35));
24422  flex-shrink:0;
24423}
24424
24425.slot-top-fixed-footer .footer-nav-icon svg{
24426  width:100%;
24427  height:100%;
24428  display:block;
24429}
24430
24431.slot-top-fixed-footer a:hover img,
24432.slot-top-fixed-footer a:hover .footer-nav-icon{
24433  transform:scale(1.08);
24434  filter:drop-shadow(0 0 16px rgba(251,191,36,.6));
24435}
24436
24437.slot-top-fixed-footer .hud-corner{
24438  position:absolute;
24439  width:18px;
24440  height:18px;
24441  pointer-events:none;
24442  border:1px solid rgba(234,179,8,.5);
24443  filter:drop-shadow(0 0 10px rgba(234,179,8,.2));
24444  opacity:.85;
24445}
24446
24447.slot-top-fixed-footer .hud-tl{top:8px;left:10px;border-right:none;border-bottom:none;border-radius:10px 0 0 0;}
24448.slot-top-fixed-footer .hud-tr{top:8px;right:10px;border-left:none;border-bottom:none;border-radius:0 10px 0 0;}
24449.slot-top-fixed-footer .hud-bl{bottom:8px;left:10px;border-right:none;border-top:none;border-radius:0 0 0 10px;}
24450.slot-top-fixed-footer .hud-br{bottom:8px;right:10px;border-left:none;border-top:none;border-radius:0 0 10px 0;}
24451
24452.slot-top-fixed-footer .tada{
24453  color:#1a1400;
24454  background:linear-gradient(180deg, #F5E6A8 0%, #D4AF37 40%, #C9A227 70%, #8B6914 100%);
24455  border:1px solid rgba(212,175,55,.55);
24456  box-shadow:0 0 18px rgba(212,175,55,.45), inset 0 1px 0 rgba(255,248,220,.4), inset 0 -1px 0 rgba(0,0,0,.2);
24457}
24458
24459.slot-top-fixed-footer .tada img{
24460  width:30px;
24461  height:30px;
24462  filter:drop-shadow(0 0 12px rgba(212,175,55,.55));
24463}
24464
24465.slot-top-fixed-footer .tada::before{
24466  content:"";
24467  position:absolute;
24468  inset:-12px;
24469  border-radius:999px;
24470  background:
24471    radial-gradient(circle,#000000,transparent 70%),
24472    radial-gradient(circle,rgba(251,191,36,.08),transparent 72%);
24473  filter:blur(12px);
24474  opacity:.55;
24475  animation:corePulse 2.9s ease-in-out infinite;
24476  z-index:-1;
24477}
24478
24479@keyframes corePulse{
24480  0%,100%{opacity:.35;transform:scale(.99);}
24481  50%{opacity:.75;transform:scale(1.02);}
24482}
24483
24484.slot-top-fixed-footer a.is-active{
24485  color:#ffffff;
24486  background:rgba(234,179,8,.1);
24487  box-shadow:inset 0 0 0 1px rgba(234,179,8,.25);
24488}
24489
24490.slot-top-fixed-footer a.is-active img{
24491  filter:drop-shadow(0 0 18px rgba(251,191,36,.8));
24492}
24493
24494@media (max-width:500px){
24495  .slot-top-fixed-footer{
24496    padding:10px 10px calc(10px + env(safe-area-inset-bottom));
24497    border-radius:16px 16px 0 0;
24498    gap:6px;
24499  }
24500
24501  .slot-top-fixed-footer a{
24502    font-size:11px;
24503    padding:7px 5px;
24504    gap:5px;
24505    max-width:104px;
24506  }
24507
24508  .slot-top-fixed-footer img{
24509    width:22px;
24510    height:22px;
24511  }
24512
24513  .slot-top-fixed-footer .tada img{
24514    width:26px;
24515    height:26px;
24516  }
24517}
24518</style>
24519<div class="slot-top-fixed-footer">
24520  <a href="https://relaksasi.norita.mx/" target="_blank" rel="nofollow noopener">
24521    <img src="/img/favicon.png" alt="BONUS EDUTOTO">
24522    Promosi
24523  </a>
24524  <a href="https://relaksasi.norita.mx/" target="_blank" rel="nofollow noopener">
24525    <img src="/img/favicon.png" alt="LOGIN EDUTOTO">
24526    Login
24527  </a>
24528  <a href="https://relaksasi.norita.mx/" target="_blank" rel="nofollow noopener" class="tada">
24529    <img src="/img/favicon.png" alt="DAFTAR EDUTOTO">
24530    Daftar
24531  </a>
24532  <a href="https://relaksasi.norita.mx/" target="_blank" rel="nofollow noopener">
24533    <img src="/img/favicon.png" alt="RTP EDUTOTO">
24534    RTP
24535  </a>
24536  <a href="https://relaksasi.norita.mx/" target="_blank" rel="nofollow noopener">
24537    <img src="/img/favicon.png" alt="LIVE CHAT EDUTOTO">
24538    Live Chat
24539  </a>
24540</div>
24541<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"d2b233a7815a4287aaa052abb87bc709","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 2 bytes, line 24541
24541
24542<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"a53b173d9c24451dab6bb5be1849fbba","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 2 bytes, line 24542
24542
24543<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v4513226cdae34746b4dedf0b4dfa099e1781791509496" integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ==" data-cf-beacon='{"version":"2024.11.0","token":"89fcf4ab9804493f841f6843fd186fff","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
vendor: 2 bytes, line 24543
24543
24544<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4bc70e2c01a94c73b74392e4234840661791215815920" integrity="sha512-L0ha0OXavK/8okipN9F8BtP84dg9DUhPERbBXzwI6dgTA55d2+yweo3pn5CSFYs45/r8md2+xvUPtTdvTNRfjA==" data-cf-beacon='{"version":"2024.11.0","token":"a879294c6a484e56a7b1c10ea9ba2706","r":1,"spa":2}' crossorigin="anonymous"></script>
24544
24545</body>
24546</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.